KOOM 源码分析
目錄
一、版本
二、類圖
三、流程
3.1 KOOM初始化流程 + 內(nèi)存監(jiān)控流程
3.2 內(nèi)存觸發(fā)分析流程
3.3 內(nèi)存快照分析流程
四、總結(jié)
五、收益
六、參考
一、版本
implementation 'com.kwai.koom:java-oom:1.0.7'二、類圖
三、流程
3.1 KOOM初始化流程 + 內(nèi)存監(jiān)控流程
KOOM.java/*** KOOM entry point, make sure be called in the main thread!** @param application application needed*/public static void init(Application application) {KLog.init(new KLog.DefaultLogger());if (inited) {KLog.i(TAG, "already init!");return;}inited = true;if (koom == null) {//初始化單例類koom = new KOOM(application);}koom.start();}/*** Start KOOM.*/public void start() {internal.start();}==== KOOMInternal.javapublic void start() {HandlerThread koomThread = new HandlerThread("koom");//開(kāi)啟一個(gè)HandlerThread線程koomThread.start();koomHandler = new Handler(koomThread.getLooper());startInKOOMThread();}private void startInKOOMThread() {//10秒之后開(kāi)啟任務(wù)koomHandler.postDelayed(this::startInternal, KConstants.Perf.START_DELAY);}private void startInternal() {if (started) {KLog.i(TAG, "already started!");return;}started = true;//設(shè)置內(nèi)存dump觸發(fā)器的回調(diào)監(jiān)聽(tīng)heapDumpTrigger.setHeapDumpListener(this);//設(shè)置內(nèi)存分析觸發(fā)器的回調(diào)監(jiān)聽(tīng)heapAnalysisTrigger.setHeapAnalysisListener(this);//檢測(cè)是否滿足開(kāi)啟內(nèi)存監(jiān)控的條件 (補(bǔ)充1)if (KOOMEnableChecker.doCheck() != KOOMEnableChecker.Result.NORMAL) {KLog.e(TAG, "koom start failed, check result: " + KOOMEnableChecker.doCheck());return;}//如果上一次寫(xiě)入的內(nèi)存分析json文件沒(méi)有完成,再一觸發(fā)內(nèi)存分析ReanalysisChecker reanalysisChecker = new ReanalysisChecker();//(補(bǔ)充2)if (reanalysisChecker.detectReanalysisFile() != null) {KLog.i(TAG, "detected reanalysis file");heapAnalysisTrigger.trigger(TriggerReason.analysisReason(TriggerReason.AnalysisReason.REANALYSIS));return;}//開(kāi)啟內(nèi)存檢測(cè)heapDumpTrigger.startTrack();}補(bǔ)充1
KOOMEnableChecker.java/*** Check if KOOM can start.** @return check result*/public static Result doCheck() {runningChecker = get();if (runningChecker.result != null) {return runningChecker.result;}//檢測(cè)系統(tǒng) >=LoLLIPOP && <= Qif (!runningChecker.isVersionPermit()) {return runningChecker.result = Result.OS_VERSION_NO_COMPATIBILITY;}//檢測(cè)磁盤(pán)空間 >= 5GBif (!runningChecker.isSpaceEnough()) {return runningChecker.result = Result.SPACE_NOT_ENOUGH;}//檢測(cè)app第一次啟動(dòng)之后的15天之內(nèi)if (runningChecker.isDateExpired()) {return runningChecker.result = Result.EXPIRED_DATE;}//檢測(cè)默認(rèn)每個(gè)用戶可以內(nèi)存分析3次if (runningChecker.isMaxTimesOverflow()) {return runningChecker.result = Result.EXPIRED_TIMES;}//檢測(cè)是否在主進(jìn)程開(kāi)啟if (!runningChecker.isProcessPermitted()) {return runningChecker.result = Result.PROCESS_NOT_ENABLED;}return Result.NORMAL;}補(bǔ)充2
public KHeapFile detectReanalysisFile() {File reportDir = new File(KGlobalConfig.getReportDir());File[] reports = reportDir.listFiles();if (reports == null) {return null;}for (File report : reports) {//解析內(nèi)存分析文件,解析成HeapReport對(duì)象HeapReport heapReport = loadFile(report);//判斷是否解析完成if (analysisNotDone(heapReport)) {if (!overReanalysisMaxTimes(heapReport)) {KLog.i(TAG, "find reanalyze report");return buildKHeapFile(report);} else {KLog.e(TAG, "Reanalyze " + report.getName() + " too many times");//Reanalyze too many times, and the hporf is abnormal, so delete them.File hprof = findHprof(getReportFilePrefix(report));if (hprof != null) {hprof.delete();}report.delete();}}}return null;}HeapDumpTrigger
HeapDumpTrigger.java@Overridepublic void startTrack() {monitorManager.start();//內(nèi)存條件滿足時(shí),觸發(fā)時(shí)回調(diào)監(jiān)聽(tīng)monitorManager.setTriggerListener((monitorType, reason) -> {trigger(reason);return true;});}====MonitorManager.javapublic void start() {monitorThread.start(monitors);}==== MonitorThread.javapublic MonitorThread() {//內(nèi)存監(jiān)控子線程thread = new HandlerThread("MonitorThread");thread.start();handler = new Handler(thread.getLooper());}public void start(List<Monitor> monitors) {stop = false;Log.i(TAG, "start");List<Runnable> runnables = new ArrayList<>();for (Monitor monitor : monitors) {monitor.start();runnables.add(new MonitorRunnable(monitor));}for (Runnable runnable : runnables) {handler.post(runnable);}}==== MonitorRunnable.java@Overridepublic void run() {//stop標(biāo)識(shí)符 = true時(shí),停止內(nèi)存監(jiān)控if (stop) {return;}if (KConstants.Debug.VERBOSE_LOG) {Log.i(TAG, monitor.monitorType() + " monitor run");}//內(nèi)存監(jiān)控是否觸發(fā) (補(bǔ)充1)if (monitor.isTrigger()) {Log.i(TAG, monitor.monitorType() + " monitor "+ monitor.monitorType() + " trigger");//觸發(fā)回調(diào)stop = monitorTriggerListener.onTrigger(monitor.monitorType(), monitor.getTriggerReason());}//如果沒(méi)有停止時(shí),循環(huán)運(yùn)行,默認(rèn)POLL_INTERVAL = 5000if (!stop) {handler.postDelayed(this, monitor.pollInterval());}}補(bǔ)充1
HeapMonitor.java@Overridepublic boolean isTrigger() {if (!started) {return false;}HeapStatus heapStatus = currentHeapStatus();//判斷應(yīng)用內(nèi)存是否超過(guò)設(shè)置的內(nèi)存閾值if (heapStatus.isOverThreshold) {KLog.i(TAG, "heap status used:" + heapStatus.used / KConstants.Bytes.MB+ ", max:" + heapStatus.max / KConstants.Bytes.MB+ ", last over times:" + currentTimes);//默認(rèn)trueif (heapThreshold.ascending()) {if (lastHeapStatus == null || heapStatus.used >= lastHeapStatus.used) {//內(nèi)存比上一次增加時(shí)currentTimes++;} else {KLog.i(TAG, "heap status used is not ascending, and over times reset to 0");//有一次下降,則計(jì)數(shù)重置=0currentTimes = 0;}} else {currentTimes++;}} else {currentTimes = 0;}lastHeapStatus = heapStatus;//次數(shù)大于設(shè)置的超過(guò)閾值的次數(shù)時(shí)return currentTimes >= heapThreshold.overTimes();}3.2 內(nèi)存觸發(fā)分析流程
HeapDumpTrigger.java @Overridepublic void startTrack() {monitorManager.start();monitorManager.setTriggerListener((monitorType, reason) -> {//觸發(fā)分析trigger(reason);return true;});}@Overridepublic void trigger(TriggerReason reason) {if (triggered) {KLog.e(TAG, "Only once trigger!");return;}triggered = true;//停止內(nèi)存監(jiān)控monitorManager.stop();KLog.i(TAG, "trigger reason:" + reason.dumpReason);if (heapDumpListener != null) {//狀態(tài)lisenter回調(diào)heapDumpListener.onHeapDumpTrigger(reason.dumpReason);}try {//Dump內(nèi)存doHeapDump(reason.dumpReason);} catch (Exception e) {KLog.e(TAG, "doHeapDump failed");e.printStackTrace();if (heapDumpListener != null) {heapDumpListener.onHeapDumpFailed();}}KVData.addTriggerTime(KGlobalConfig.getRunningInfoFetcher().appVersion());}public void doHeapDump(TriggerReason.DumpReason reason) {KLog.i(TAG, "doHeapDump");//創(chuàng)建本地hprof文件、report文件KHeapFile.getKHeapFile().buildFiles();//report文件中json對(duì)象添加dump理由HeapAnalyzeReporter.addDumpReason(reason);//report文件添加設(shè)備基本信息HeapAnalyzeReporter.addDeviceRunningInfo();boolean res = heapDumper.dump(KHeapFile.getKHeapFile().hprof.path);if (res) {heapDumpListener.onHeapDumped(reason);} else {KLog.e(TAG, "heap dump failed!");heapDumpListener.onHeapDumpFailed();KHeapFile.delete();}} ForkJvmHeapDumper.java@Overridepublic boolean dump(String path) {KLog.i(TAG, "dump " + path);if (!soLoaded) {KLog.e(TAG, "dump failed caused by so not loaded!");return false;}if (!KOOMEnableChecker.get().isVersionPermit()) {KLog.e(TAG, "dump failed caused by version net permitted!");return false;}if (!KOOMEnableChecker.get().isSpaceEnough()) {KLog.e(TAG, "dump failed caused by disk space not enough!");return false;}boolean dumpRes = false;try {//暫停虛擬機(jī),copy-on-write fork子進(jìn)程int pid = trySuspendVMThenFork();//子進(jìn)程中if (pid == 0) {//核心Api:系統(tǒng)提供的dump內(nèi)存快照的方法Debug.dumpHprofData(path);KLog.i(TAG, "notifyDumped:" + dumpRes);//System.exit(0);//退出子進(jìn)程exitProcess();} else {//父進(jìn)程中//resume當(dāng)前虛擬機(jī)resumeVM();//waitpid異步等待pid進(jìn)程結(jié)束dumpRes = waitDumping(pid);KLog.i(TAG, "hprof pid:" + pid + " dumped: " + path);}} catch (IOException e) {e.printStackTrace();KLog.e(TAG, "dump failed caused by IOException!");}return dumpRes;}疑問(wèn)1:在ForkJvmHeapDumper類中的dump()方法,怎么去的子進(jìn)程和父進(jìn)程的操作的?按常理要么在子進(jìn)程中,要么在父進(jìn)程中?
?
3.3 內(nèi)存快照分析流程
KOOMInternal.java@Overridepublic void onHeapDumped(TriggerReason.DumpReason reason) {KLog.i(TAG, "onHeapDumped");changeProgress(KOOMProgressListener.Progress.HEAP_DUMPED);//Crash cases need to reanalyze next launch and not do analyze right now.if (reason != TriggerReason.DumpReason.MANUAL_TRIGGER_ON_CRASH) {heapAnalysisTrigger.startTrack();} else {KLog.i(TAG, "reanalysis next launch when trigger on crash");}}===== HeapAnalysisTrigger.java@Overridepublic void startTrack() {KTriggerStrategy strategy = strategy();if (strategy == KTriggerStrategy.RIGHT_NOW) {trigger(TriggerReason.analysisReason(TriggerReason.AnalysisReason.RIGHT_NOW));}}@Overridepublic void trigger(TriggerReason triggerReason) {//do trigger when foregroundif (!isForeground) {KLog.i(TAG, "reTrigger when foreground");this.reTriggerReason = triggerReason;return;}KLog.i(TAG, "trigger reason:" + triggerReason.analysisReason);if (triggered) {KLog.i(TAG, "Only once trigger!");return;}triggered = true;//添加分析理由HeapAnalyzeReporter.addAnalysisReason(triggerReason.analysisReason);if (triggerReason.analysisReason == TriggerReason.AnalysisReason.REANALYSIS) {HeapAnalyzeReporter.recordReanalysis();}//test reanalysis//if (triggerReason.analysisReason != TriggerReason.AnalysisReason.REANALYSIS) return;if (heapAnalysisListener != null) {heapAnalysisListener.onHeapAnalysisTrigger();}try {//開(kāi)始分析doAnalysis(KGlobalConfig.getApplication());} catch (Exception e) {KLog.e(TAG, "doAnalysis failed");e.printStackTrace();if (heapAnalysisListener != null) {heapAnalysisListener.onHeapAnalyzeFailed();}}}public void doAnalysis(Application application) {HeapAnalyzeService.runAnalysis(application, heapAnalysisListener);}==== HeapAnalyzeService.java//啟動(dòng)Service服務(wù)public static void runAnalysis(Application application,HeapAnalysisListener heapAnalysisListener) {KLog.i(TAG, "runAnalysis startService");Intent intent = new Intent(application, HeapAnalyzeService.class);IPCReceiver ipcReceiver = buildAnalysisReceiver(heapAnalysisListener);intent.putExtra(KConstants.ServiceIntent.RECEIVER, ipcReceiver);KHeapFile heapFile = KHeapFile.getKHeapFile();intent.putExtra(KConstants.ServiceIntent.HEAP_FILE, heapFile);application.startService(intent);}@Overrideprotected void onHandleIntent(Intent intent) {KLog.i(TAG, "start analyze pid:" + android.os.Process.myPid());boolean res = false;try {//解析前先獲取intent里面?zhèn)鬟f的IPCreceiver和heapFile對(duì)象beforeAnalyze(intent);res = doAnalyze();} catch (Throwable e) {e.printStackTrace();}if (ipcReceiver != null) {//(補(bǔ)充1)ipcReceiver.send(res ? IPCReceiver.RESULT_CODE_OK: IPCReceiver.RESULT_CODE_FAIL, null);}}/*** run in the heap_analysis process*/private boolean doAnalyze() {return heapAnalyzer.analyze();}補(bǔ)充1
由于HeapAnalyzeService服務(wù)聲明在heap_analysis進(jìn)程中,所以在HeapAnalyzeService分析內(nèi)存的結(jié)果回調(diào)是通過(guò)IPCreceicer extends ResultReceiver進(jìn)行回調(diào)結(jié)果值的
java-oom.aar中<application><serviceandroid:name="com.kwai.koom.javaoom.analysis.HeapAnalyzeService"//單獨(dú)進(jìn)程android:process=":heap_analysis" /></application>//通過(guò)ResultReceiver獲取服務(wù)進(jìn)程的分析結(jié)果class IPCReceiver extends ResultReceiver {}開(kāi)始分析:
KHeapAnalyzer.javaprivate SuspicionLeaksFinder leaksFinder;public boolean analyze() {KLog.i(TAG, "analyze");Pair<List<ApplicationLeak>, List<LibraryLeak>> leaks = leaksFinder.find();if (leaks == null) {return false;}//Add gc path to report file.HeapAnalyzeReporter.addGCPath(leaks, leaksFinder.leakReasonTable);//Add done flag to report file.HeapAnalyzeReporter.done();return true;}==== SuspicionLeaksFinder.javapublic Pair<List<ApplicationLeak>, List<LibraryLeak>> find() {//(補(bǔ)充1)// 根據(jù)內(nèi)存快照,使用shark庫(kù)建立heapGraph對(duì)象boolean indexed = buildIndex();if (!indexed) {return null;}// (補(bǔ)充2)//初始化探測(cè)器initLeakDetectors();//發(fā)現(xiàn)泄漏findLeaks();return findPath();}public void findLeaks() {KLog.i(TAG, "start find leaks");//遍歷鏡像的所有instanceSequence<HeapObject.HeapInstance> instances = heapGraph.getInstances();Iterator<HeapObject.HeapInstance> instanceIterator = instances.iterator();while (instanceIterator.hasNext()) {HeapObject.HeapInstance instance = instanceIterator.next();if (instance.isPrimitiveWrapper()) {continue;}ClassHierarchyFetcher.process(instance.getInstanceClassId(),instance.getInstanceClass().getClassHierarchy());for (LeakDetector leakDetector : leakDetectors) {if (leakDetector.isSubClass(instance.getInstanceClassId())// (補(bǔ)充3)//是否存在泄漏&& leakDetector.isLeak(instance)) {ClassCounter classCounter = leakDetector.instanceCount();if (classCounter.leakInstancesCount <=SAME_CLASS_LEAK_OBJECT_GC_PATH_THRESHOLD) {leakingObjects.add(instance.getObjectId());leakReasonTable.put(instance.getObjectId(), leakDetector.leakReason());}}}}//關(guān)注class和對(duì)應(yīng)instance數(shù)量,加入jsonHeapAnalyzeReporter.addClassInfo(leakDetectors);findPrimitiveArrayLeaks();findObjectArrayLeaks();}private void findPrimitiveArrayLeaks() {//查找基本類型數(shù)組Iterator<HeapObject.HeapPrimitiveArray> iterator = heapGraph.getPrimitiveArrays().iterator();while (iterator.hasNext()) {HeapObject.HeapPrimitiveArray array = iterator.next();int arraySize = array.getArrayLength();//如果原始數(shù)組 >= 256 * 1024;//基本數(shù)組大小閾值, 那么假如到內(nèi)存泄漏集合中if (arraySize >= DEFAULT_BIG_PRIMITIVE_ARRAY) {String arrayName = array.getArrayClassName();String typeName = array.getPrimitiveType().toString();KLog.e(TAG, "primitive arrayName:" + arrayName + " typeName:" + typeName+ " objectId:" + (array.getObjectId() & 0xffffffffL)+ " arraySize:" + arraySize);leakingObjects.add(array.getObjectId());leakReasonTable.put(array.getObjectId(), "primitive array size over threshold:"+ arraySize + "," + arraySize / KConstants.Bytes.KB + "KB");}}}private void findObjectArrayLeaks() {//查找對(duì)象數(shù)組Iterator<HeapObject.HeapObjectArray> iterator = heapGraph.getObjectArrays().iterator();while (iterator.hasNext()) {HeapObject.HeapObjectArray array = iterator.next();int arraySize = array.getArrayLength();//如果對(duì)象大小 >= 256 * 1024;//對(duì)象數(shù)組大小閾值, 假如到內(nèi)存泄漏集合中if (arraySize >= DEFAULT_BIG_OBJECT_ARRAY) {String arrayName = array.getArrayClassName();KLog.i(TAG, "object arrayName:" + arrayName+ " objectId:" + array.getObjectId());leakingObjects.add(array.getObjectId());leakReasonTable.put(array.getObjectId(), "object array size " +"over threshold:" + arraySize);}}}@SuppressWarnings("unchecked")public Pair<List<ApplicationLeak>, List<LibraryLeak>> findPath() {KLog.i(TAG, "findPath object size:" + leakingObjects.size());HeapAnalyzer.FindLeakInput findLeakInput = new HeapAnalyzer.FindLeakInput(heapGraph,AndroidReferenceMatchers.Companion.getAppDefaults(),false, Collections.emptyList());kotlin.Pair<List<ApplicationLeak>, List<LibraryLeak>> pair =new HeapAnalyzer(step -> KLog.i(TAG, "step:" + step.name()))//根據(jù)泄漏集合存儲(chǔ)的objectId,遍歷獲取泄漏鏈路.findLeaks(findLeakInput, leakingObjects, true);//返回應(yīng)用泄漏路徑集合和庫(kù)泄漏路徑集合return new Pair<>((List<ApplicationLeak>) pair.getFirst(),(List<LibraryLeak>) pair.getSecond());} KHeapAnalyzer.java public boolean analyze() {KLog.i(TAG, "analyze");Pair<List<ApplicationLeak>, List<LibraryLeak>> leaks = leaksFinder.find();if (leaks == null) {return false;}//Add gc path to report file.//報(bào)告對(duì)象文件寫(xiě)入泄漏GCPathHeapAnalyzeReporter.addGCPath(leaks, leaksFinder.leakReasonTable);//Add done flag to report file.//報(bào)告對(duì)象文件寫(xiě)入分析完成標(biāo)識(shí)符HeapAnalyzeReporter.done();return true;}==== HeapAnalyzeService.java@Overrideprotected void onHandleIntent(Intent intent) {KLog.i(TAG, "start analyze pid:" + android.os.Process.myPid());boolean res = false;try {beforeAnalyze(intent);res = doAnalyze();} catch (Throwable e) {e.printStackTrace();}if (ipcReceiver != null) {//分析報(bào)告結(jié)果回調(diào)ipcReceiver.send(res ? IPCReceiver.RESULT_CODE_OK: IPCReceiver.RESULT_CODE_FAIL, null);}}==== IPCReceiver.java@Overrideprotected void onReceiveResult(int resultCode, Bundle resultData) {super.onReceiveResult(resultCode, resultData);if (receiverCallBack != null) {//返回結(jié)果if (resultCode == RESULT_CODE_OK) {receiverCallBack.onSuccess();} else {receiverCallBack.onError();}}}==== HeapAnalyzeService.javaprivate static IPCReceiver buildAnalysisReceiver(final HeapAnalysisListener heapAnalysisListener) {//回調(diào)結(jié)果return new IPCReceiver(new ReceiverCallback() {public void onSuccess() {KLog.i("HeapAnalyzeService", "IPC call back, heap analysis success");heapAnalysisListener.onHeapAnalyzed();}public void onError() {KLog.i("HeapAnalyzeService", "IPC call back, heap analysis failed");heapAnalysisListener.onHeapAnalyzeFailed();}});}==== KOOMInternal.java@Overridepublic void onHeapAnalyzed() {KLog.i(TAG, "onHeapAnalyzed");changeProgress(KOOMProgressListener.Progress.HEAP_ANALYSIS_DONE);uploadFiles(KHeapFile.getKHeapFile());}@Overridepublic void onHeapAnalyzeFailed() {changeProgress(KOOMProgressListener.Progress.HEAP_ANALYSIS_FAILED);}private void uploadFiles(KHeapFile heapFile) {uploadHprof(heapFile.hprof);uploadHeapReport(heapFile.report);}private void uploadHprof(KHeapFile.Hprof hprof) {if (hprofUploader != null) {//自己的App設(shè)置監(jiān)聽(tīng)接受內(nèi)存快照文件回調(diào)進(jìn)行處理hprofUploader.upload(hprof.file());}//Do not save the hprof file by default.if (hprofUploader == null || hprofUploader.deleteWhenUploaded()) {KLog.i(TAG, "delete " + hprof.path);//刪除內(nèi)存快照文件hprof.delete();}}private void uploadHeapReport(KHeapFile.Report report) {if (heapReportUploader != null) {//自己的App設(shè)置監(jiān)聽(tīng)接受HeapReport內(nèi)存上報(bào)文件回調(diào)進(jìn)行處理heapReportUploader.upload(report.file());}//Save the report file by default.if (heapReportUploader != null && heapReportUploader.deleteWhenUploaded()) {KLog.i(TAG, "report delete");//刪除內(nèi)存報(bào)告文件report.delete();}}?
補(bǔ)充1
private boolean buildIndex() {KLog.i(TAG, "build index file:" + hprofFile.path);if (hprofFile.file() == null || !hprofFile.file().exists()) {KLog.e(TAG, "hprof file is not exists : " + hprofFile.path + "!!");return false;}Hprof hprof = Hprof.Companion.open(hprofFile.file());KClass<GcRoot>[] gcRoots = new KClass[]{Reflection.getOrCreateKotlinClass(GcRoot.JniGlobal.class),//Reflection.getOrCreateKotlinClass(GcRoot.JavaFrame.class),Reflection.getOrCreateKotlinClass(GcRoot.JniLocal.class),//Reflection.getOrCreateKotlinClass(GcRoot.MonitorUsed.class),Reflection.getOrCreateKotlinClass(GcRoot.NativeStack.class),Reflection.getOrCreateKotlinClass(GcRoot.StickyClass.class),Reflection.getOrCreateKotlinClass(GcRoot.ThreadBlock.class),Reflection.getOrCreateKotlinClass(GcRoot.ThreadObject.class),Reflection.getOrCreateKotlinClass(GcRoot.JniMonitor.class)};//生成一個(gè)堆圖索引對(duì)象heapGraph = HprofHeapGraph.Companion.indexHprof(hprof, null,kotlin.collections.SetsKt.setOf(gcRoots));return true;}==== HprofHeapGraph.kt該類在com.kwai.koom:shark:1.0.7中補(bǔ)充2:
private void initLeakDetectors() {//Activity泄漏探測(cè)器addDetector(new ActivityLeakDetector(heapGraph));//Fragment泄漏探測(cè)器addDetector(new FragmentLeakDetector(heapGraph));//BitMap泄漏探測(cè)器addDetector(new BitmapLeakDetector(heapGraph));//Native內(nèi)存分配泄漏探測(cè)器addDetector(new NativeAllocationRegistryLeakDetector(heapGraph));//窗口泄漏探測(cè)器addDetector(new WindowLeakDetector(heapGraph));ClassHierarchyFetcher.initComputeGenerations(computeGenerations);leakReasonTable = new HashMap<>();}補(bǔ)充3:
ActivityLeakDetector.java@Overridepublic boolean isLeak(HeapObject.HeapInstance instance) {if (VERBOSE_LOG) {KLog.i(TAG, "run isLeak");}activityCounter.instancesCount++;HeapField destroyField = instance.get(ACTIVITY_CLASS_NAME, DESTROYED_FIELD_NAME);HeapField finishedField = instance.get(ACTIVITY_CLASS_NAME, FINISHED_FIELD_NAME);assert destroyField != null;assert finishedField != null;boolean abnormal = destroyField.getValue().getAsBoolean() == null|| finishedField.getValue().getAsBoolean() == null;if (abnormal) {KLog.e(TAG, "ABNORMAL destroyField or finishedField is null");return false;}// destroy和finished為true時(shí),但是Activity對(duì)象還在內(nèi)存中,表示泄漏了boolean leak = destroyField.getValue().getAsBoolean()|| finishedField.getValue().getAsBoolean();if (leak) {if (VERBOSE_LOG) {KLog.e(TAG, "activity leak : " + instance.getInstanceClassName());}activityCounter.leakInstancesCount++;}return leak;}==== FragmentLeakDetector.java@Overridepublic boolean isLeak(HeapObject.HeapInstance instance) {if (VERBOSE_LOG) {KLog.i(TAG, "run isLeak");}fragmentCounter.instancesCount++;boolean leak = false;HeapField fragmentManager = instance.get(fragmentClassName, FRAGMENT_MANAGER_FIELD_NAME);if (fragmentManager != null && fragmentManager.getValue().getAsObject() == null) {HeapField mCalledField = instance.get(fragmentClassName, FRAGMENT_MCALLED_FIELD_NAME);boolean abnormal = mCalledField == null || mCalledField.getValue().getAsBoolean() == null;if (abnormal) {KLog.e(TAG, "ABNORMAL mCalledField is null");return false;}//mCalled為true時(shí),但fragment對(duì)象還在內(nèi)存中,表示泄漏了leak = mCalledField.getValue().getAsBoolean();if (leak) {if (VERBOSE_LOG) {KLog.e(TAG, "fragment leak : " + instance.getInstanceClassName());}fragmentCounter.leakInstancesCount++;}}return leak;}==== BitmapLeakDetector.java@Overridepublic boolean isLeak(HeapObject.HeapInstance instance) {if (VERBOSE_LOG) {KLog.i(TAG, "run isLeak");}bitmapCounter.instancesCount++;HeapField fieldWidth = instance.get(BITMAP_CLASS_NAME, "mWidth");HeapField fieldHeight = instance.get(BITMAP_CLASS_NAME, "mHeight");assert fieldHeight != null;assert fieldWidth != null;boolean abnormal = fieldHeight.getValue().getAsInt() == null|| fieldWidth.getValue().getAsInt() == null;if (abnormal) {KLog.e(TAG, "ABNORMAL fieldWidth or fieldHeight is null");return false;}int width = fieldWidth.getValue().getAsInt();int height = fieldHeight.getValue().getAsInt();//bitmap位圖大小 >= 768*1366時(shí),懷疑有泄漏boolean suspicionLeak = width * height >= KConstants.BitmapThreshold.DEFAULT_BIG_BITMAP;if (suspicionLeak) {KLog.e(TAG, "bitmap leak : " + instance.getInstanceClassName() + " " +"width:" + width + " height:" + height);bitmapCounter.leakInstancesCount++;}return suspicionLeak;}==== NativeAllocationRegistryLeakDetector.java//暫未實(shí)現(xiàn)@Overridepublic boolean isLeak(HeapObject.HeapInstance instance) {if (!supported) {return false;}nativeAllocationCounter.instancesCount++;return false;}==== WindowLeakDetector.java//暫未實(shí)現(xiàn)@Overridepublic boolean isLeak(HeapObject.HeapInstance instance) {if (VERBOSE_LOG) {KLog.i(TAG, "run isLeak");}windowCounter.instancesCount++;return false;}四、總結(jié)
| 優(yōu)點(diǎn) | 1.使用了自己的內(nèi)存檢測(cè)策略,將內(nèi)存泄漏的檢測(cè)延后 ? ?1.1配置內(nèi)存閾值 ? ?1.2配置內(nèi)存最大值 |
| ? | 2.使用子進(jìn)程進(jìn)行內(nèi)存dump,不用阻塞主進(jìn)程,避免UI線程卡頓 |
| ? | 3.內(nèi)存分析庫(kù)使用了shark庫(kù),對(duì)內(nèi)存進(jìn)行裁剪優(yōu)化 |
| ? | 4.自動(dòng)分成內(nèi)存泄漏文件,生成json格式的文件方便用戶使用 |
| ? | ? |
| 缺點(diǎn) | 1.暫未支持底層Native的泄漏監(jiān)聽(tīng) |
?
?
?
?
?
?
?
五、收益
| 設(shè)計(jì)模式 | 外觀模式 內(nèi)部使用用KOOMInternal類進(jìn)行實(shí)現(xiàn)總體流程的控制 |
| ? | 策略模式 使用LeakDetector抽象類,具體實(shí)現(xiàn)使用AcvitiyLeakDetector, FragmentLeakDetector, WindowLeakDetector, BitmapLeakDetector 進(jìn)行內(nèi)存泄漏判斷 |
| ? | 構(gòu)造者模式 用戶自定義Builder Config |
| ? | ? |
| 設(shè)計(jì) | 1.使用HandlerThread開(kāi)啟子線程進(jìn)行異步任務(wù)操作,接合設(shè)置Listener回調(diào)進(jìn)行子線程與UI線程出來(lái) |
| ? | 2.跨進(jìn)程通信通過(guò)使用核心API:ResultReceiver進(jìn)行進(jìn)程間的通信 |
?
?
?
?
六、參考
https://www.jianshu.com/p/018febffb06f
總結(jié)
- 上一篇: 各种语言的特点和介绍(附图
- 下一篇: 平衡二叉树理论详解