Koom流程记录(一)

时间:2022-07-23
本文章向大家介绍Koom流程记录(一),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
Koom 官网:
https://github.com/KwaiAppTeam/KOOM

KOOM(Kwai OOM, Kill OOM)是快手性能优化团队在处理移动端OOM问题的过程中沉淀出的一套完整解决方案。

文章对 Koom 做一个简单的流程记录

参考的是KOOM-1.0.5版本

KOOM.init(this)

在 APP 启动的时候 开启一个HandlerThread, 10s 之后开始分析

1.如果之前崩溃过 ,或者本地有hporf文件直接分析

2.否则再开启一个HandlerThread, 开始监控内存变化

class KOOMInternal implements HeapDumpListener, HeapAnalysisListener {
    
    //handle.post 来控制执行时机,10s 之后执行
    private void startInternal() {
        //...
        if (KOOMEnableChecker.doCheck() != KOOMEnableChecker.Result.NORMAL) {
            KLog.e(TAG, "koom start failed, check result: " + KOOMEnableChecker.doCheck());
            return;
        }
        //1.如果之前崩溃过 ,本地有hporf文件直接分析
        ReanalysisChecker reanalysisChecker = new ReanalysisChecker();
        //本地KHeapFile 存在,直接再分析一次 ,否则开启监控
        if (reanalysisChecker.detectReanalysisFile() != null) {
            KLog.i(TAG, "detected reanalysis file");
            heapAnalysisTrigger
                    .trigger(TriggerReason.analysisReason(TriggerReason.AnalysisReason.REANALYSIS));
            return;
        }
        //2.否则再开启一个HandlerThread作为监控线程, 开始监控内存变化
        // 直接调度MonitorManager
        heapDumpTrigger.startTrack();
    }
}
如何确定阈值

不同的机型 设置不同的阈值,用百分比来表示占用的内存

public class KConstants {
    
    public static float getDefaultPercentRation() {
      //获取最大可用内存
      int maxMem = (int) (Runtime.getRuntime().maxMemory() / MB);
      if (Debug.VERBOSE_LOG) {
        KLog.i("koom", "max mem " + maxMem);
      }
      // 512 MB 的设备 阈值百分比是80
      if (maxMem >= VM_512_DEVICE) {
        return HeapThreshold.PERCENT_RATIO_IN_512_DEVICE;
      } else if (maxMem >= VM_256_DEVICE) {
        //256MB 的设备 阈值百分比是85
        return HeapThreshold.PERCENT_RATIO_IN_256_DEVICE;
      } else if (maxMem >= VM_128_DEVICE) {
        //128MB的设备 阈值百分比是90
        return HeapThreshold.PERCENT_RATIO_IN_128_DEVICE;
      }
      // 其他 阈值百分比一律为80
      return HeapThreshold.PERCENT_RATIO_IN_512_DEVICE;
    }
}
监控Manager

在MonitorManager中开启监控线程,监控线程也是一个HandlerThread

public class MonitorManager {
   
  public void start() {
    //monitors集合只有一个HeapMonitor对象
    monitorThread.start(monitors);
  } 
}
MonitorThread
public class MonitorThread {
    
    public void start(List<Monitor> monitors) {
        //...
        List<Runnable> runnables = new ArrayList<>();
        for (Monitor monitor : monitors) {
            //获取HeapMonitor的heapThreshold
            monitor.start();
            runnables.add(new MonitorRunnable(monitor));
        }
        //执行 list 中的Runnable,因为只有一个,就是MonitorRunnable了
        for (Runnable runnable : runnables) {
            // 使用handler 发送MonitorRunnable, 结果就是执行MonitorRunnable其 run()方法
            handler.post(runnable);
        }
    }
    //
    class MonitorRunnable implements Runnable {
        //...
        @Override
        public void run() {
            // if 分支为 true 说明 APP 占用内存使用超过阈值 3 次
            if (monitor.isTrigger()) {
                // 这里 stop 恒为 true
                stop = monitorTriggerListener
                        .onTrigger(monitor.monitorType(), monitor.getTriggerReason());
            }
            // 上面的 if 分支不被执行 ,stop一直为 false
            // 那么当前run()方法,每隔 5s 就会执行一次 形成一个循环
            if (!stop) {
                handler.postDelayed(this, monitor.pollInterval());
            }
        }
    }
}

MonitorThread 的核心就是MonitorRunnable的 run 方法了.

1.HeapMonitor 发现 超过阈值 3 次则触发内存分析

2.否则每个 5s 检测一次内存是否超过阈值

说一下HeapThreshold

public class HeapThreshold implements Threshold {
  //根据不同的手机的最大内存,返回不同的值
  private float heapRatioInPercent;
  //默认 3 超过这个次数 开始内存分析
  private int overTimes;
  // HeapMonitor检测的频率 默认 5s
  private int pollInterval;
}
HeapDumpTrigger 分析HeapDump

上面的MonitorRunnable在循环执行中一旦次数>3:

1.MonitorThread 终止循环

2.触发monitorTriggerListener.onTrigger()方法回调

public class HeapDumpTrigger implements KTrigger {
 
   @Override
   public void trigger(TriggerReason reason) {
 
     if (heapDumpListener != null) {
       // 对外回调回调进度 Progress.HEAP_DUMP_START
       heapDumpListener.onHeapDumpTrigger(reason.dumpReason);
     }
 
     // 分析HeapDump
     doHeapDump(reason.dumpReason);
   }
   //
  public void doHeapDump(TriggerReason.DumpReason reason) {
    // 构建KHeapFile
    KHeapFile.getKHeapFile().buildFiles();

    HeapAnalyzeReporter.addDumpReason(reason);
    // 收集设备信息
    HeapAnalyzeReporter.addDeviceRunningInfo();
    
    // 关键 代码
    boolean res = heapDumper.dump(KHeapFile.getKHeapFile().hprof.path);

    if (res) {
      //对外回调回调进度 Progress.HEAP_DUMPED
      heapDumpListener.onHeapDumped(reason);
    } else {
      KLog.e(TAG, "heap dump failed!");
      //对外回调回调进度 Progress.HEAP_DUMP_FAILED
      heapDumpListener.onHeapDumpFailed();
      KHeapFile.delete();
    }
  }
}
ForkJvmHeapDumper
public class ForkJvmHeapDumper implements HeapDumper {
   @Override
   public boolean dump(String path) {
     //...
     try {
       int pid = trySuspendVMThenFork();
       if (pid == 0) {
        //...
       } else {
         resumeVM();
         // 关键代码
         dumpRes = waitDumping(pid);
       }
 
     } catch (Exception e) {
     }
     return dumpRes;
   }   
}
分析结果

类似如下:

{
	"runningInfo": {
		"analysisReason": "RIGHT_NOW",
		"appVersion": "1.0",
		"buildModel": "iPhone12 Pro",
		"currentPage": "MainActivity",
		"dumpReason": "HEAP_OVER_THRESHOLD",
		"jvmMax": 256,
		"jvmUsed": 192,
		"koomVersion": 1,
		"manufacture": "iPhone12Mobile",
		"nowTime": "2020-08-25_22-04-35",
		"pss": 200,
		"rss": 282,
		"sdkInt": 23,
		"threadCount": 31,
		"usageSeconds": 37,
		"vss": 1966
	}
}
其他配置
public class MyApplication extends Application {
    
    @Override
    public void onCreate() {
        super.onCreate();
        KOOM.init(this);
        //增加配置,方便看结果
        KOOM.getInstance().setKConfig(new KConfig.KConfigBuilder().heapRatio(30f).heapOverTimes(2).build());
    }
}
相关知识

Debug.getPss()

Runtime.getRuntime().maxMemory()

Runtime.getRuntime().freeMemory()

PSS

RSS

so 库加载失败

以上见相关文章

更多内容 欢迎关注公众号
qrcode_for_gh_9e4ad64366d5_258.jpg