[TOC]
# 內存問題
## 內存泄漏
內存泄露是指沒有用的對象由于某種原因程序未釋放或無法釋放,造成系統內存的浪費。Android常見的場景有
* 非靜態內部類隱式持有外部類的引用導致的泄漏,包括:
2. 非靜態內部類的靜態實例
3. 多線程相關的匿名內部類/非靜態內部類 解決辦法:變靜態
4. Handler 解決辦法1.變靜態 2.在onDestory移除MessageQueue mHander.removeCallbacksAndMessages(null)
* 靜態View
* WebView,只要使用過一次的WebView,內存就不會被釋放。解決辦法:為webView單獨開一個進程
* 資源未釋放,如File、Cursor,往往使用可緩存。解決辦法:在finally中進行關閉
* bitmap
* 無限制的使用緩存
* 系統組件導致的泄漏,如[InputMethodManager 導致的泄漏](https://www.jianshu.com/p/62e498075e56),[Drawable泄露](https://www.jianshu.com/p/914888735ce5)。
* 線程過度使用,線程初始化都需要 mmap 一定的 stack size,
## 內存抖動
內存抖動是由于短時間內有大量對象進出新生區導致的,它伴隨著頻繁的GC。?
gc會大量占用ui線程和cpu資源,會導致app整體卡頓
## 內存溢出 OOM
### 堆空間不足發生OOM
1. 當你的app啟動后,系統會分配給app一個堆空間,起始不會很大比如是32M(根據你的app啟動時的內存申請為準)
2. 隨著程序的運行對象的創建越來越多,系統不斷加內存分配:32M -> 64M -> ...
3. 而GC回收則會定時掃描內存,發現不被引用的對象即可回收。正常來說你的app堆內存會有升有降。
4. app運行一段時間堆內存超過系統規定的最大值 heapSize,杯子滿了就會發現內存溢OOM,app崩潰。
### heapSize大小
我們用下面的代碼獲取heapsize:
~~~
ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int heapSize = manager.getMemoryClass();
int maxHeapSize = manager.getLargeMemoryClass(); // manafest.xml android:largeHeap="true"
~~~
heapSize是設備分配給app的最大堆內存
maxHeapSize 是當配置了android:largeHeap="true" 才有的最大堆內存,一般是heapSize的2-3倍
以HTC ONE為例,兩個值分別是: 192M和512M。這里我只關注192M,
### 線程無法申請OOM(堆內存充裕下發生OOM)
~~~
java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Out of memory
~~~
每個線程初始化都需要 mmap 一定的 stack size,在默認的情況下一般初始化一個線程需要 mmap 1M 左右的內存空間,在 32位的系統中有 4g 的 vmsize,實際能使用的有 3g+,按這種估算,一個進程最大能創建的線程數可達 3000+,當然這是理想的情況,在 linux 中對每個進程可創建的線程數也有一定的限制(/proc/pid/limits)而實際測試中,我們也發現不同廠商對這個限制也有所不同,而且當超過系統進程線程數限制時,同樣會拋出這個類型的 OOM。
**可見對線程數量的限制,可以一定程度避免 OOM 的發生**。所以我們也開始對微信的線程數進行了監控統計。
### OOM是否可以try catch
只有在一種情況下,這樣做是可行的:
在try語句中聲明了很大的對象,導致OOM,并且可以確認OOM是由try語句中的對象聲明導致的,那么在catch語句中,可以釋放掉這些對象,解決OOM的問題,繼續執行剩余語句。
但是這通常不是合適的做法。
Java中管理內存除了顯式地catch OOM之外還有更多有效的方法:比如SoftReference, WeakReference, 硬盤緩存等。
在JVM用光內存之前,會多次觸發GC,這些GC會降低程序運行的效率。
如果**OOM的原因不是try語句中的對象(比如內存泄漏)**,那么在catch語句中會繼續拋出OOM
# 內存優化
## 常規手段
內存優化的一些細節問題可以在開發時避免,下面介紹一些常規的內存優化手段
1)、使用`LargeHeap`屬性增加最大可用內存
2)、在系統觸發資源緊張回調時,主動刪除緩存
3)、使用優化過后的集合:如`SparseArray`類等
4)、謹慎使用 `SharedPreference`,`SP`會在應用初始化時將所有內容加載到內存中,所以不應該存放比較大的內容
5)、謹慎使用外部庫,引入時需要明確不會對應用性能造成大的影響
6)、業務架構設計要合理,抽象可以優化代碼的靈活性和可維護性,但是抽象也會帶來其他成本,應權衡使用
## 圖片優化
見【圖片內存優化】
# 線上內存監控指標
## 內存泄漏
## 內存觸底率
## 大圖監控
## native內存監控
# 參考資料
[Android進階性能調優;不可思議的OOM](https://www.jianshu.com/p/31f207782915)
[微信 Android 終端內存優化實踐](https://mp.weixin.qq.com/s/KtGfi5th-4YHOZsEmTOsjg)
[性能優化(三)看完這篇文章,至少解決 APP 中 90 % 的內存異常問題](https://juejin.im/post/5cd82a3ee51d456e781f20ce)
[【從入門到實用】android內存優化深入解析](https://juejin.cn/post/6975876569990447134)
- Android
- 四大組件
- Activity
- Fragment
- Service
- 序列化
- Handler
- Hander介紹
- MessageQueue詳細
- 啟動流程
- 系統啟動流程
- 應用啟動流程
- Activity啟動流程
- View
- view繪制
- view事件傳遞
- choreographer
- LayoutInflater
- UI渲染概念
- Binder
- Binder原理
- Binder最大數據
- Binder小結
- Android組件
- ListView原理
- RecyclerView原理
- SharePreferences
- AsyncTask
- Sqlite
- SQLCipher加密
- 遷移與修復
- Sqlite內核
- Sqlite優化v2
- sqlite索引
- sqlite之wal
- sqlite之鎖機制
- 網絡
- 基礎
- TCP
- HTTP
- HTTP1.1
- HTTP2.0
- HTTPS
- HTTP3.0
- HTTP進化圖
- HTTP小結
- 實踐
- 網絡優化
- Json
- ProtoBuffer
- 斷點續傳
- 性能
- 卡頓
- 卡頓監控
- ANR
- ANR監控
- 內存
- 內存問題與優化
- 圖片內存優化
- 線下內存監控
- 線上內存監控
- 啟動優化
- 死鎖監控
- 崩潰監控
- 包體積優化
- UI渲染優化
- UI常規優化
- I/O監控
- 電量監控
- 第三方框架
- 網絡框架
- Volley
- Okhttp
- 網絡框架n問
- OkHttp原理N問
- 設計模式
- EventBus
- Rxjava
- 圖片
- ImageWoker
- Gilde的優化
- APT
- 依賴注入
- APT
- ARouter
- ButterKnife
- MMKV
- Jetpack
- 協程
- MVI
- Startup
- DataBinder
- 黑科技
- hook
- 運行期Java-hook技術
- 編譯期hook
- ASM
- Transform增量編譯
- 運行期Native-hook技術
- 熱修復
- 插件化
- AAB
- Shadow
- 虛擬機
- 其他
- UI自動化
- JavaParser
- Android Line
- 編譯
- 疑難雜癥
- Android11滑動異常
- 方案
- 工業化
- 模塊化
- 隱私合規
- 動態化
- 項目管理
- 業務啟動優化
- 業務架構設計
- 性能優化case
- 性能優化-排查思路
- 性能優化-現有方案
- 登錄
- 搜索
- C++
- NDK入門
- 跨平臺
- H5
- Flutter
- Flutter 性能優化
- 數據跨平臺