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 库加载失败
以上见相关文章
更多内容 欢迎关注公众号
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- scRNA-seq marker identification(一)
- 关于linux权限s权限和t权限详解
- centOS7 桥接模式设置静态Ip的方法步骤
- linux环境下卸载oracle 11g的过程
- Seurat包基本分析实战—文献图表复现
- ubuntu配置tftp服务的步骤小结
- CentOS7下GitLab跨大版本升级的方法
- 解决Linux system v 共享内存问题
- Linux下core文件的使用方法详解
- 使用Kubeadm在CentOS7.2上部署Kubernetes集群的方法
- linux systemctl命令详解
- CentOS7使用dnf安装mysql的方法
- Linux中crontab定时任务不执行的原因
- Linux系统为什么要吃掉我的“内存”
- 教你如何修改Linux远程登录欢迎提示信息