[TOC]
# 線上OOM監控方案
## LeakCanary缺點
`LeakCanary`在線下監測內存泄漏,但是`LeakCanary`只能在線下使用,有以下問題
線下場景能跑到的場景有限,很難把所有用戶場景窮盡.碰到線上問題難以定位
2.檢測過程需要主動觸發`GC`,`Dump`內存鏡像造成`app`凍結,造成測試過程中體驗不好
3.適用范圍有限,只能定位`Activity`&`Fragment`泄漏,無法定位大對象、頻繁分配等問題
4.`hprof`文件過大,如果整體上傳的話需要耗費很多資源
上面我們介紹了`LeakCanary`不能用于線上監控的原因,所以要實現線上監控功能,就需要解決以下問題
* 監控
* 主動觸發`GC`,會造成卡頓
* 采集
* `Dump hprof`,會造成`app`凍結
* `Hprof`文件過大
* 解析
* 解析耗時過長
* 解析本身有`OOM`風險
其核心流程為三部分:
1.監控`OOM`,發生問題時觸發內存鏡像的采集,以便進一步分析問題
2.采集內存鏡像,學名堆轉儲,將內存數據拷貝到文件中,以下簡稱`dump hprof`
3.解析鏡像文件,對泄漏、超大對象等我們關注的對象進行可達性分析,解析出其到`GC root`的引用鏈以解決問題

## KOOM解決`GC`卡頓
`LeakCanary`通過多次`GC`的方式來判斷對象是否被回收,所以會造成性能損耗
`koom`通過無性能損耗的內存閾值監控來觸發鏡像采集,具體策略如下:
* `Java`堆內存/線程數/文件描述符數突破閾值觸發采集
* `Java`堆上漲速度突破閾值觸發采集
* 發生`OOM`時如果策略1、2未命中 觸發采集
* 泄漏判定延遲至解析時
我們并不需要在運行時判定對象是否泄漏,以`Activity`為例,我們并不需要在運行時判定其是否泄漏,`Activity`有一個成員變`mDestroyed`,在`onDestory`時會被置為`true`,只要解析時發現有可達且`mDestroyed`為`true`的`Activity`,即可判定為泄漏
通過將泄漏判斷延遲至解析時,即可解決`GC`卡頓的問題

## `KOOM`解決`Dump hprof`凍結`app`
`Dump hprof`即采集內存鏡像需要暫停虛擬機,以確保在內存數據拷貝到磁盤的過程中,引用關系不會發生變化,暫停時間通常長達10秒以上,對用戶來講是難以接受的,這也是`LeakCanary`官方不推薦線上使用的重要原因之一。
利用`Copy-on-write`機制,`fork`子進程`dump`內存鏡像,可以完美解決這一問題,`fork`成功以后,父進程立刻恢復虛擬機運行,子進程`dump`內存鏡像期間不會受到父進程數據變動的影響。
流程如下圖所示:

`KOOM`隨機采集線上真實用戶的內存鏡像,普通`dump`和`fork`子進程`dump`阻塞用戶使用的耗時如下:

可以看出,基本可以做到無感知的采集內存鏡像
## `KOOM`解決`hprof`文件過大
`Hprof`文件通常比較大,分析`OOM`時遇到500M以上的`hprof`文件并不稀奇,文件的大小,與`dump`成功率、`dump`速度、上傳成功率負相關,且大文件額外浪費用戶大量的磁盤空間和流量。
因此需要對`hprof`進行裁剪,只保留分析`OOM`必須的數據,另外,裁剪還有數據脫敏的好處,只上傳內存中類與對象的組織結構,并不上傳真實的業務數據(諸如字符串、`byte`數組等含有具體數據的內容),保護用戶隱私。
裁剪`hprof`文件涉及到對`hprof`文件格式的了解,這里就不綴述了
下面看一下裁剪過程的流程圖:

## `KOOM`解決`hprof`解析的耗時與`OOM`
解析`hprof`文件,對關鍵對象進行可達性分析,得到引用鏈,是解決`OOM`最核心的一步,之前的監控和`dump`都是為解析做鋪墊。
解析分兩種,一種是上傳`hprof`文件由`server`解析,另一種是在客戶端解析后上傳報告(通常只有幾`KB`)。
`KOOM`選擇了端上解析,這樣做有兩個好處:
* 1.節省用戶流量
* 2.利用用戶閑時算力,降低`server`壓力,這樣也符合分布式計算理念。
這樣就可以把解析過程拆解成以下兩個問題
* 哪些對象需要分析,全部分析性能開銷太大,很難在端上完成,并且問題沒有重點也不利于解決。
* 性能優化,作為一個`debug`組件,要在不影響用戶體驗的情況下完成解析,對性能有非常高的要求。
### 關鍵對象判定
`KOOM`只解析關鍵的對象,關鍵對象分為兩類,一類是根據規則可以判斷出對象已經泄露,且持有大量資源的,另外一類是對象`shallow / retained size` 超過閾值
`Activity/fragment`泄露判定即為第一種:
對于強可達的`activity`對象,其`mDestroyed`值為`true`時(`onDestroy`時賦值),判定已經泄露。
類似的,對于`fragment`,當`mCalled`值為`true`且`mFragmentManager`為`null`時,判定已經泄露 。
`Bitmap/window/array/sufacetexture`判定為第二種
檢查`bitmap/texture`的數量、寬高、`window`數量、`array`長度等等是否超過閾值,再結合`hprof`中的相關業務信息,比如屏幕大小,`view`大小等進行判定。
### 性能優化
`KOOM`在`LeakCanary`解析引擎`shark`的基礎上做了一些優化,將解析時間在`shark`的基礎上優化了2倍以上,內存峰值控制在100M以內。 用一張圖總結解析的流程:

詳細流程就不在這里綴述了,詳情可見:[KOOM解析性能優化](https://juejin.cn/post/6860014199973871624#heading-13 "https://juejin.cn/post/6860014199973871624#heading-13")
### `KOOM`使用
`KOOM`目前已經開源,開源地址:[github.com/KwaiAppTeam…](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2FKwaiAppTeam%2FKOOM "https://github.com/KwaiAppTeam/KOOM")
直接參照接入指南接入即可,當發現內存超過閾值或者發生`OOM`時,就會觸發采集內存快照,對`hprof`文件進行裁剪并分析后得到報告
`KOOM`的報告是`json`格式,并且大小在`KB`級別,樣式如下所示:

大概包括以下信息
1.一些可能泄漏的類信息
2.泄漏原因,`gcRoot`,泄漏實例數量等
3.泄漏對象的引用鏈,方便定位問題
可見`KOOM`上傳的數據量并不太大,但相對準確,非常便于我們分析線上數據
# 參考資料
[【從入門到實用】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 性能優化
- 數據跨平臺