KOOM 源码分析

KOOM是一款内存监控与泄漏检测框架,它通过初始化、内存触发分析和快照分析流程来检测内存问题。框架在主线程外进行内存dump,使用shark库分析内存快照,寻找应用和库的内存泄漏。其优点包括延迟检测策略、子进程dump内存和生成泄漏报告。然而,目前不支持Native泄漏检测。
摘要由CSDN通过智能技术生成

目录

一、版本

二、类图

三、流程

3.1 KOOM初始化流程 + 内存监控流程

3.2 内存触发分析流程

3.3 内存快照分析流程

四、总结

五、收益

六、参考


一、版本

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的泄漏监听

 

 

 

 

 

 

 

五、收益

设计模式

外观模式
使用KOOM, KOOMInternal2个类,外部暴露KOOM类给用户进行配置,

内部使用用KOOMInternal类进行实现总体流程的控制

 

策略模式

使用LeakDetector抽象类,具体实现使用AcvitiyLeakDetector, FragmentLeakDetector, WindowLeakDetector, BitmapLeakDetector 进行内存泄漏判断

 构造者模式
用户自定义Builder Config
  
设计1.使用HandlerThread开启子线程进行异步任务操作,接合设置Listener回调进行子线程与UI线程出来
 2.跨进程通信通过使用核心API:ResultReceiver进行进程间的通信

 

 

 

 

六、参考

https://www.jianshu.com/p/018febffb06f

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值