目录
一、版本
implementation 'com.kwai.koom:java-oom:1.0.7'
二、类图
三、流程
3.1 KOOM初始化流程 + 内存监控流程
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.java
public void start() {
HandlerThread koomThread = new HandlerThread("koom");
//开启一个HandlerThread线程
koomThread.start();
koomHandler = new Handler(koomThread.getLooper());
startInKOOMThread();
}
private void startInKOOMThread() {
//10秒之后开启任务
koomHandler.postDelayed(this::startInternal, KConstants.Perf.START_DELAY);
}
private void startInternal() {
if (started) {
KLog.i(TAG, "already started!");
return;
}
started = true;
//设置内存dump触发器的回调监听
heapDumpTrigger.setHeapDumpListener(this);
//设置内存分析触发器的回调监听
heapAnalysisTrigger.setHeapAnalysisListener(this);
//检测是否满足开启内存监控的条件 (补充1)
if (KOOMEnableChecker.doCheck() != KOOMEnableChecker.Result.NORMAL) {
KLog.e(TAG, "koom start failed, check result: " + KOOMEnableChecker.doCheck());
return;
}
//如果上一次写入的内存分析json文件没有完成,再一触发内存分析
ReanalysisChecker reanalysisChecker = new ReanalysisChecker();
//(补充2)
if (reanalysisChecker.detectReanalysisFile() != null) {
KLog.i(TAG, "detected reanalysis file");
heapAnalysisTrigger
.trigger(TriggerReason.analysisReason(TriggerReason.AnalysisReason.REANALYSIS));
return;
}
//开启内存检测
heapDumpTrigger.startTrack();
}
补充1
KOOMEnableChecker.java
/**
* Check if KOOM can start.
*
* @return check result
*/
public static Result doCheck() {
runningChecker = get();
if (runningChecker.result != null) {
return runningChecker.result;
}
//检测系统 >=LoLLIPOP && <= Q
if (!runningChecker.isVersionPermit()) {
return runningChecker.result = Result.OS_VERSION_NO_COMPATIBILITY;
}
//检测磁盘空间 >= 5GB
if (!runningChecker.isSpaceEnough()) {
return runningChecker.result = Result.SPACE_NOT_ENOUGH;
}
//检测app第一次启动之后的15天之内
if (runningChecker.isDateExpired()) {
return runningChecker.result = Result.EXPIRED_DATE;
}
//检测默认每个用户可以内存分析3次
if (runningChecker.isMaxTimesOverflow()) {
return runningChecker.result = Result.EXPIRED_TIMES;
}
//检测是否在主进程开启
if (!runningChecker.isProcessPermitted()) {
return runningChecker.result = Result.PROCESS_NOT_ENABLED;
}
return Result.NORMAL;
}
补充2
public KHeapFile detectReanalysisFile() {
File reportDir = new File(KGlobalConfig.getReportDir());
File[] reports = reportDir.listFiles();
if (reports == null) {
return null;
}
for (File report : reports) {
//解析内存分析文件,解析成HeapReport对象
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
@Override
public void startTrack() {
monitorManager.start();
//内存条件满足时,触发时回调监听
monitorManager.setTriggerListener((monitorType, reason) -> {
trigger(reason);
return true;
});
}
====
MonitorManager.java
public void start() {
monitorThread.start(monitors);
}
====
MonitorThread.java
public MonitorThread() {
//内存监控子线程
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
@Override
public void run() {
//stop标识符 = true时,停止内存监控
if (stop) {
return;
}
if (KConstants.Debug.VERBOSE_LOG) {
Log.i(TAG, monitor.monitorType() + " monitor run");
}
//内存监控是否触发 (补充1)
if (monitor.isTrigger()) {
Log.i(TAG, monitor.monitorType() + " monitor "
+ monitor.monitorType() + " trigger");
//触发回调
stop = monitorTriggerListener
.onTrigger(monitor.monitorType(), monitor.getTriggerReason());
}
//如果没有停止时,循环运行,默认POLL_INTERVAL = 5000
if (!stop) {
handler.postDelayed(this, monitor.pollInterval());
}
}
补充1
HeapMonitor.java
@Override
public boolean isTrigger() {
if (!started) {
return false;
}
HeapStatus heapStatus = currentHeapStatus();
//判断应用内存是否超过设置的内存阈值
if (heapStatus.isOverThreshold) {
KLog.i(TAG, "heap status used:" + heapStatus.used / KConstants.Bytes.MB
+ ", max:" + heapStatus.max / KConstants.Bytes.MB
+ ", last over times:" + currentTimes);
//默认true
if (heapThreshold.ascending()) {
if (lastHeapStatus == null || heapStatus.used >= lastHeapStatus.used) {
//内存比上一次增加时
currentTimes++;
} else {
KLog.i(TAG, "heap status used is not ascending, and over times reset to 0");
//有一次下降,则计数重置=0
currentTimes = 0;
}
} else {
currentTimes++;
}
} else {
currentTimes = 0;
}
lastHeapStatus = heapStatus;
//次数大于设置的超过阈值的次数时
return currentTimes >= heapThreshold.overTimes();
}
3.2 内存触发分析流程
HeapDumpTrigger.java
@Override
public void startTrack() {
monitorManager.start();
monitorManager.setTriggerListener((monitorType, reason) -> {
//触发分析
trigger(reason);
return true;
});
}
@Override
public void trigger(TriggerReason reason) {
if (triggered) {
KLog.e(TAG, "Only once trigger!");
return;
}
triggered = true;
//停止内存监控
monitorManager.stop();
KLog.i(TAG, "trigger reason:" + reason.dumpReason);
if (heapDumpListener != null) {
//状态lisenter回调
heapDumpListener.onHeapDumpTrigger(reason.dumpReason);
}
try {
//Dump内存
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");
//创建本地hprof文件、report文件
KHeapFile.getKHeapFile().buildFiles();
//report文件中json对象添加dump理由
HeapAnalyzeReporter.addDumpReason(reason);
//report文件添加设备基本信息
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
@Override
public 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 {
//暂停虚拟机,copy-on-write fork子进程
int pid = trySuspendVMThenFork();
//子进程中
if (pid == 0) {
//核心Api:系统提供的dump内存快照的方法
Debug.dumpHprofData(path);
KLog.i(TAG, "notifyDumped:" + dumpRes);
//System.exit(0);
//退出子进程
exitProcess();
} else {//父进程中
//resume当前虚拟机
resumeVM();
//waitpid异步等待pid进程结束
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;
}
疑问1:在ForkJvmHeapDumper类中的dump()方法,怎么去的子进程和父进程的操作的?按常理要么在子进程中,要么在父进程中?
3.3 内存快照分析流程
KOOMInternal.java
@Override
public 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
@Override
public void startTrack() {
KTriggerStrategy strategy = strategy();
if (strategy == KTriggerStrategy.RIGHT_NOW) {
trigger(TriggerReason.analysisReason(TriggerReason.AnalysisReason.RIGHT_NOW));
}
}
@Override
public void trigger(TriggerReason triggerReason) {
//do trigger when foreground
if (!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 {
//开始分析
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
//启动Service服务
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);
}
@Override
protected void onHandleIntent(Intent intent) {
KLog.i(TAG, "start analyze pid:" + android.os.Process.myPid());
boolean res = false;
try {
//解析前先获取intent里面传递的IPCreceiver和heapFile对象
beforeAnalyze(intent);
res = doAnalyze();
} catch (Throwable e) {
e.printStackTrace();
}
if (ipcReceiver != null) {
//(补充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();
}
补充1
由于HeapAnalyzeService服务声明在heap_analysis进程中,所以在HeapAnalyzeService分析内存的结果回调是通过IPCreceicer extends ResultReceiver进行回调结果值的
java-oom.aar中
<application>
<service
android:name="com.kwai.koom.javaoom.analysis.HeapAnalyzeService"
//单独进程
android:process=":heap_analysis" />
</application>
//通过ResultReceiver获取服务进程的分析结果
class IPCReceiver extends ResultReceiver {}
开始分析:
KHeapAnalyzer.java
private 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.java
public Pair<List<ApplicationLeak>, List<LibraryLeak>> find() {
//(补充1)
// 根据内存快照,使用shark库建立heapGraph对象
boolean indexed = buildIndex();
if (!indexed) {
return null;
}
// (补充2)
//初始化探测器
initLeakDetectors();
//发现泄漏
findLeaks();
return findPath();
}
public void findLeaks() {
KLog.i(TAG, "start find leaks");
//遍历镜像的所有instance
Sequence<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())
// (补充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());
}
}
}
}
//关注class和对应instance数量,加入json
HeapAnalyzeReporter.addClassInfo(leakDetectors);
findPrimitiveArrayLeaks();
findObjectArrayLeaks();
}
private void findPrimitiveArrayLeaks() {
//查找基本类型数组
Iterator<HeapObject.HeapPrimitiveArray> iterator = heapGraph.getPrimitiveArrays().iterator();
while (iterator.hasNext()) {
HeapObject.HeapPrimitiveArray array = iterator.next();
int arraySize = array.getArrayLength();
//如果原始数组 >= 256 * 1024;//基本数组大小阈值, 那么假如到内存泄漏集合中
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() {
//查找对象数组
Iterator<HeapObject.HeapObjectArray> iterator = heapGraph.getObjectArrays().iterator();
while (iterator.hasNext()) {
HeapObject.HeapObjectArray array = iterator.next();
int arraySize = array.getArrayLength();
//如果对象大小 >= 256 * 1024;//对象数组大小阈值, 假如到内存泄漏集合中
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()))
//根据泄漏集合存储的objectId,遍历获取泄漏链路
.findLeaks(findLeakInput, leakingObjects, true);
//返回应用泄漏路径集合和库泄漏路径集合
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.
//报告对象文件写入泄漏GCPath
HeapAnalyzeReporter.addGCPath(leaks, leaksFinder.leakReasonTable);
//Add done flag to report file.
//报告对象文件写入分析完成标识符
HeapAnalyzeReporter.done();
return true;
}
====
HeapAnalyzeService.java
@Override
protected 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) {
//分析报告结果回调
ipcReceiver.send(res ? IPCReceiver.RESULT_CODE_OK
: IPCReceiver.RESULT_CODE_FAIL, null);
}
}
====
IPCReceiver.java
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (receiverCallBack != null) {
//返回结果
if (resultCode == RESULT_CODE_OK) {
receiverCallBack.onSuccess();
} else {
receiverCallBack.onError();
}
}
}
====
HeapAnalyzeService.java
private static IPCReceiver buildAnalysisReceiver(final HeapAnalysisListener heapAnalysisListener) {
//回调结果
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
@Override
public void onHeapAnalyzed() {
KLog.i(TAG, "onHeapAnalyzed");
changeProgress(KOOMProgressListener.Progress.HEAP_ANALYSIS_DONE);
uploadFiles(KHeapFile.getKHeapFile());
}
@Override
public 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设置监听接受内存快照文件回调进行处理
hprofUploader.upload(hprof.file());
}
//Do not save the hprof file by default.
if (hprofUploader == null || hprofUploader.deleteWhenUploaded()) {
KLog.i(TAG, "delete " + hprof.path);
//删除内存快照文件
hprof.delete();
}
}
private void uploadHeapReport(KHeapFile.Report report) {
if (heapReportUploader != null) {
//自己的App设置监听接受HeapReport内存上报文件回调进行处理
heapReportUploader.upload(report.file());
}
//Save the report file by default.
if (heapReportUploader != null && heapReportUploader.deleteWhenUploaded()) {
KLog.i(TAG, "report delete");
//删除内存报告文件
report.delete();
}
}
补充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)};
//生成一个堆图索引对象
heapGraph = HprofHeapGraph.Companion.indexHprof(hprof, null,
kotlin.collections.SetsKt.setOf(gcRoots));
return true;
}
====
HprofHeapGraph.kt
该类在com.kwai.koom:shark:1.0.7中
补充2:
private void initLeakDetectors() {
//Activity泄漏探测器
addDetector(new ActivityLeakDetector(heapGraph));
//Fragment泄漏探测器
addDetector(new FragmentLeakDetector(heapGraph));
//BitMap泄漏探测器
addDetector(new BitmapLeakDetector(heapGraph));
//Native内存分配泄漏探测器
addDetector(new NativeAllocationRegistryLeakDetector(heapGraph));
//窗口泄漏探测器
addDetector(new WindowLeakDetector(heapGraph));
ClassHierarchyFetcher.initComputeGenerations(computeGenerations);
leakReasonTable = new HashMap<>();
}
补充3:
ActivityLeakDetector.java
@Override
public 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时,但是Activity对象还在内存中,表示泄漏了
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
@Override
public 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时,但fragment对象还在内存中,表示泄漏了
leak = mCalledField.getValue().getAsBoolean();
if (leak) {
if (VERBOSE_LOG) {
KLog.e(TAG, "fragment leak : " + instance.getInstanceClassName());
}
fragmentCounter.leakInstancesCount++;
}
}
return leak;
}
====
BitmapLeakDetector.java
@Override
public 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时,怀疑有泄漏
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
//暂未实现
@Override
public boolean isLeak(HeapObject.HeapInstance instance) {
if (!supported) {
return false;
}
nativeAllocationCounter.instancesCount++;
return false;
}
====
WindowLeakDetector.java
//暂未实现
@Override
public boolean isLeak(HeapObject.HeapInstance instance) {
if (VERBOSE_LOG) {
KLog.i(TAG, "run isLeak");
}
windowCounter.instancesCount++;
return false;
}
四、总结
优点 | 1.使用了自己的内存检测策略,将内存泄漏的检测延后 1.1配置内存阈值 1.2配置内存最大值 |
2.使用子进程进行内存dump,不用阻塞主进程,避免UI线程卡顿 | |
3.内存分析库使用了shark库,对内存进行裁剪优化 | |
4.自动分成内存泄漏文件,生成json格式的文件方便用户使用 | |
缺点 | 1.暂未支持底层Native的泄漏监听 |
五、收益
设计模式 | 外观模式 内部使用用KOOMInternal类进行实现总体流程的控制 |
策略模式 使用LeakDetector抽象类,具体实现使用AcvitiyLeakDetector, FragmentLeakDetector, WindowLeakDetector, BitmapLeakDetector 进行内存泄漏判断 | |
构造者模式 用户自定义Builder Config | |
设计 | 1.使用HandlerThread开启子线程进行异步任务操作,接合设置Listener回调进行子线程与UI线程出来 |
2.跨进程通信通过使用核心API:ResultReceiver进行进程间的通信 |