https://github.com/KwaiAppTeam/KOOM
KOOM(Kwai OOM, Kill OOM)是快手性能优化团队在处理移动端OOM问题的过程中沉淀出的一套完整解决方案。
文章对 Koom 做一个简单的流程记录
参考的是KOOM-1.0.5版本
在 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;
}
}
在MonitorManager中开启监控线程,监控线程也是一个HandlerThread
public class MonitorManager {
public void start() {
//monitors集合只有一个HeapMonitor对象
monitorThread.start(monitors);
}
}
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;
}
上面的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();
}
}
}
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 库加载失败
以上见相关文章
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。