内存泄露(十)-- KOOM(高性能线上内存监控方案)
KOOM介紹
OOMMonitor 介紹
KOOM線上APM監(jiān)控最全剖析
一文看懂快手KOOM高性能監(jiān)控方案
KOOM 淺析
【開源庫(kù)剖析】KOOM V1.0.5 源碼解析
目錄
- KOOM 功能
- 1. Java Heap 泄漏監(jiān)控
- 1.1 OOMMonitor 介紹
- 1.2 OOMMonitor 適用范圍
- 1.3 OOMMonitor 接入
- 2. Native Heap 泄漏監(jiān)控
- 2.1 LeakMonitor 介紹
- 2.2 LeakMonitor 適用范圍
- 2.3 LeakMonitor 接入
- 3. Thread 泄漏監(jiān)控
- 3.1 ThreadLeakMonitor 介紹
- 3.2 ThreadLeakMonitor 適用范圍
- 3.3 ThreadLeakMonitor 接入
??KOOM(Kwai OOM, Kill OOM)是快手性能優(yōu)化團(tuán)隊(duì)在處理移動(dòng)端OOM問題的過程中沉淀出的一套完整解決方案。
KOOM 功能
Koom流程:
OOM問題原因分類
1. Java Heap 泄漏監(jiān)控
??koom-java-leak 模塊用于 Java Heap 泄漏監(jiān)控:它利用 Copy-on-write 機(jī)制 fork 子進(jìn)程 dump Java Heap,解決了 dump 過程中 app 長(zhǎng)時(shí)間凍結(jié)的問題
- fork子進(jìn)程去執(zhí)行dumpHprofData方法
- fork進(jìn)程采用的是“Copy On Write”技術(shù),只有在進(jìn)行寫入操作時(shí),才會(huì)為子進(jìn)程拷貝分配獨(dú)立的內(nèi)存空間,默認(rèn)情況下,子進(jìn)程可以和父進(jìn)程共享同個(gè)內(nèi)存空間,所以,當(dāng)我們要執(zhí)行dumpHprofData方法時(shí),可以先fork一個(gè)子進(jìn)程,它擁有父進(jìn)程的內(nèi)存副本,然后在子進(jìn)程中執(zhí)行dumpHprofData方法,而父進(jìn)程則可以正常繼續(xù)運(yùn)行。
??hprof是基于JVMTI實(shí)現(xiàn)的內(nèi)存分析器代理,其轉(zhuǎn)儲(chǔ)文件記錄了Java的內(nèi)存鏡像(Heap Profile),其中記錄了內(nèi)存堆詳細(xì)的使用信息,可用于分析Java程序內(nèi)存的各種性能問題。
??虛擬機(jī)提供的Debug.dumpHprofData可以將hprof文件輸出在指定的文件中,但是這個(gè)過程會(huì)“凍結(jié)”整個(gè)應(yīng)用進(jìn)程,造成數(shù)秒甚至數(shù)十秒內(nèi)用戶無法操作,即dump文件也是會(huì)產(chǎn)生STW(Stop the world),跟GC一樣會(huì)讓所有線程掛起。
HeapMonitor.java
public boolean isTrigger() {if (!started) {return false;}HeapStatus heapStatus = currentHeapStatus();if (heapStatus.isOverMaxThreshold) {// 已達(dá)到最大閥值,強(qiáng)制觸發(fā)trigger,防止后續(xù)出現(xiàn)大內(nèi)存分配導(dǎo)致OOM進(jìn)程Crash,無法觸發(fā)triggerKLog.i(TAG, "heap used is over max ratio, force trigger and over times reset to 0");currentTimes = 0;return true;}if (heapStatus.isOverThreshold) {KLog.i(TAG, "heap status used:" + heapStatus.used / KConstants.Bytes.MB+ ", max:" + heapStatus.max / KConstants.Bytes.MB+ ", last over times:" + currentTimes);if (heapThreshold.ascending()) {// 第一次進(jìn)來 或者 當(dāng)前內(nèi)存占用率跟上次高 或者 當(dāng)前內(nèi)存占用率超過了最大的閾值(95%)if (lastHeapStatus == null || heapStatus.used >= lastHeapStatus.used || heapStatus.isOverMaxThreshold) {currentTimes++;} else {KLog.i(TAG, "heap status used is not ascending, and over times reset to 0");currentTimes = 0;}} else {currentTimes++;}} else {currentTimes = 0;}lastHeapStatus = heapStatus;return currentTimes >= heapThreshold.overTimes();}Koom中設(shè)置了五種檢測(cè)器
① HeapOOMTracker
② ThreadOOMTracker
③ FdOOMTracker
④ PhysicalMemoryOOMTracker
⑤ FastHugeMemoryOOMTracker
前三種稱為長(zhǎng)期高內(nèi)存檢測(cè)器,檢測(cè)機(jī)制是當(dāng)計(jì)算出內(nèi)存占用率之后,我們看下面的一個(gè)判斷條件,如果內(nèi)存占用率超過我們?cè)O(shè)定的一個(gè)閾值(例如0.8),而且當(dāng)前內(nèi)存占用率跟上次比較超過了千分之5,那么mOverThresholdCount變量就會(huì)自增1。
Heap監(jiān)測(cè)機(jī)制是第一次進(jìn)來 或者 當(dāng)前內(nèi)存占用率跟上次高 或者
當(dāng)前內(nèi)存占用率超過了最大的閾值(95%),三個(gè)條件只要滿足一個(gè),那么mOverThresholdCount變量就會(huì)自增1
??因?yàn)闄z測(cè)是一個(gè)循環(huán)的過程,所以當(dāng)?shù)谝淮芜M(jìn)來的時(shí)候,一定會(huì)自增1,而且會(huì)將本次的內(nèi)存占用率賦值給mLastHeapRatio,當(dāng)下次進(jìn)來的時(shí)候,如果內(nèi)存占用率較上次降低了,那么就會(huì)重置。
??如此往復(fù),當(dāng)mOverThresholdCount超出我們?cè)O(shè)置的閾值(例如3次),我們就認(rèn)定系統(tǒng)發(fā)生了內(nèi)存泄漏,這個(gè)時(shí)候就需要告警,并dump內(nèi)存快照分析問題。
即 1)內(nèi)存占用超過80%,2)并且檢測(cè)到了3次,兩個(gè)條件同時(shí)滿足就會(huì)進(jìn)行dump。
第四種代碼中返回false沒有進(jìn)行處理。
第五種稱為高危內(nèi)存與快速增長(zhǎng)檢測(cè)器,檢測(cè)機(jī)制是內(nèi)存占用超過90%或者本次檢測(cè)與上次檢測(cè)內(nèi)存占用超過350M,滿足兩個(gè)條件之一就會(huì)進(jìn)行dump。
1.1 OOMMonitor 介紹
用于監(jiān)控應(yīng)用的 Java 內(nèi)存泄漏問題,它的核心原理:
- 周期性查詢Java堆內(nèi)存、線程數(shù)、文件描述符數(shù)等資源占用情況,當(dāng)連續(xù)多次超過設(shè)定閾值或突發(fā)性連續(xù)快速突破高閾值時(shí),觸發(fā)鏡像采集
- 鏡像采集采用虛擬機(jī)supend->fork虛擬機(jī)進(jìn)程->虛擬機(jī)resume->dump內(nèi)存鏡像的策略,將傳統(tǒng)Dump凍結(jié)進(jìn)程20s的時(shí)間縮減至20ms以內(nèi)
- 基于shark執(zhí)行鏡像解析,并針對(duì)shark做了一系列調(diào)整用于提升性能,在手機(jī)設(shè)備測(cè)即可執(zhí)行離線內(nèi)存泄露判定與引用鏈查找,生成分析報(bào)告
1.2 OOMMonitor 適用范圍
-
Android L 及以上(API level >= 21)
-
支持 armeabi-v7a arm64-v8a x86 x86-64
1.3 OOMMonitor 接入
OOMMonitor 接入
2. Native Heap 泄漏監(jiān)控
??koom-native-leak 模塊用于 Native Heap 泄漏監(jiān)控:它利用 Tracing garbage collection 機(jī)制分析整個(gè) Native Heap,直接輸出泄漏內(nèi)存信息「大小、分配堆棧等』;極大的降低了業(yè)務(wù)同學(xué)分析、解決內(nèi)存泄漏的成本。
思路總結(jié):
- hook malloc/free等內(nèi)存分配器方法,用于記錄Native內(nèi)存分配元數(shù)據(jù)[大小、堆棧、地址等]–使用了愛奇藝的xhook庫(kù)
- 周期性的使用mark-and-sweep分析整個(gè)進(jìn)程N(yùn)ative Heap, 獲取不可達(dá)的內(nèi)存塊信息[地址、大小]–使用到google的libmemunreachable庫(kù)
- 利用不可達(dá)的內(nèi)存塊的地址、大小等從我們記錄的元數(shù)據(jù)中獲取其分配堆棧,產(chǎn)出泄漏數(shù)據(jù)[不可達(dá)內(nèi)存地址、大小、分配堆棧等]
2.1 LeakMonitor 介紹
用于監(jiān)控應(yīng)用的 Native 內(nèi)存泄漏問題,它的核心原理如下:
- hook malloc/free 等內(nèi)存分配器方法,用于記錄 Native 內(nèi)存分配元數(shù)據(jù)「大小、堆棧、地址等」
- 周期性的使用 mark-and-sweep 分析整個(gè)進(jìn)程 Native Heap,獲取不可達(dá)的內(nèi)存塊信息「地址、大小」
- 利用不可達(dá)的內(nèi)存塊的地址、大小等從我們記錄的元數(shù)據(jù)中獲取其分配堆棧,產(chǎn)出泄漏數(shù)據(jù)「不可達(dá)內(nèi)存塊地址、大小、分配堆棧等」
2.2 LeakMonitor 適用范圍
- Android N 及以上(API level >= 24)
- 僅支持 arm64-v8a
2.3 LeakMonitor 接入
LeakMonitor 接入
3. Thread 泄漏監(jiān)控
??koom-thread-leak 模塊用于 Thread 泄漏監(jiān)控:它會(huì) hook 線程的生命周期函數(shù),周期性的上報(bào)泄漏線程信息。
3.1 ThreadLeakMonitor 介紹
用于監(jiān)控應(yīng)用的線程泄漏問題,它的核心原理:
- hook pthread_create/pthread_exit 等線程方法,用于記錄線程的生命周期和創(chuàng)建堆棧,名稱等信息
- 當(dāng)發(fā)現(xiàn)一個(gè)joinable的線程在沒有detach或者join的情況下,執(zhí)行了pthread_exit,則記錄下泄露線程信息
- 當(dāng)線程泄露時(shí)間到達(dá)配置設(shè)置的延遲期限的時(shí)候,上報(bào)線程泄露信息
3.2 ThreadLeakMonitor 適用范圍
- Android N 及以上(API level >= 24)
- 僅支持 arm64-v8a
3.3 ThreadLeakMonitor 接入
ThreadLeakMonitor 接入
總結(jié)
以上是生活随笔為你收集整理的内存泄露(十)-- KOOM(高性能线上内存监控方案)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java基于JSP野生动物保护网
- 下一篇: ”下载到itunes的软件不能同步到ip