代刷网地址

快手秒刷播放网址_快手秒刷播放网址有哪些

本文作者

作者快手秒刷播放网址: codelang

链接:

稳住,今天是周末,分享一篇浅析 KOOM 的文章,如今在问到内存相关的问题时,肯定不是说线下 LeakCanary 就行的,一般都会反问 4 连:

线上怎么监控?

什么时候 dump内存?

如何 dump 内存?

dump 下来怎么裁切、上传、分析

快手秒刷播放网址_快手秒刷播放网址有哪些

感兴趣大家可以先行阅读和体验下,后面还会有一篇非常硬核的详细源码分析。

KOOM 相比较 LeakCanary 和 Matrix 来说有点不同,后俩者由于 dump 的整个过程会影响到主进程,所以基本应用与线下监控,而 KOOM 提出了 fork dump 的概念,能在 dump 分析内存泄漏的时候而不影响到主进程的应用运行,所以,非常适合使用在线上监控。

https://github.com/KwaiAppTeam/KOOM

所有的内存泄漏监控工具都离不开这三点:

1、监控触发时机

2、dump 内存堆栈

3、分析 hprof 文件

1

监控触发时机

LeakCanary 和 Matrix 都是在Activity.onDestroy 时触发泄漏检测,KOOM 有点另辟蹊径,KOOM 是用阈值检测法来触发,我们来看下核心逻辑:

MonitorThread.class

classMonitorRunnableimplementsRunnable{

...

@Override

publicvoidrun{

if(stop) {

return;

}

// 是否触发检测

if(monitor.isTrigger) {

// 检测回调触发

stop = monitorTriggerListener

.onTrigger(monitor.monitorType, monitor.getTriggerReason);

}

if(!stop) {

// 间隔 5s 轮训检测

handler.postDelayed( this, monitor.pollInterval);

}

}

}

MonitorThread是一个利用HandlerThread 不停在轮训监控当前是否触发检测,isTrigger 是关键所在。

HeapMonitor.class

@ Override

publicboolean isTrigger{

...

// ①、获取当前的内存状态

HeapStatus heapStatus = currentHeapStatus;

// ②、当前使用内存是否达到最大阈值,内存使用占比超过 95%

if(heapStatus.isOverMaxThreshold) {

// 已达到最大阀值,强制触发 trigger,防止后续出现大内存分配导致 OOM 进程 Crash,无法触发 trigger

currentTimes = 0;

returntrue;

}

// ③、当前使用内存是否达到触发条件,内存使用占比超过 80、85、90

if(heapStatus.isOverThreshold) {

// 默认是 true

if(heapThreshold. ascending) {

// ④、此时记录的内存占用比上次记录的高、达到最大阈值

if(lastHeapStatus == null|| heapStatus.used >= lastHeapStatus.used || heapStatus.isOverMaxThreshold) {

currentTimes++;

} else{

currentTimes = 0;

}

} else{

currentTimes++;

}

} else{

currentTimes = 0;

}

// 将本地记录进行缓存

快手秒刷播放网址_快手秒刷播放网址有哪些

lastHeapStatus = heapStatus;

// ⑤、记录的次数超过 3 次,则触发条件

returncurrentTimes >= heapThreshold.overTimes;

}

privateHeapStatus lastHeapStatus;

privateHeapStatus currentHeapStatus{

HeapStatus heapStatus = newHeapStatus;

heapStatus.max = Runtime.getRuntime.maxMemory;

heapStatus.used = Runtime.getRuntime.totalMemory - Runtime.getRuntime.freeMemory;

floatheapInPercent = 100.0f * heapStatus.used / heapStatus.max;

heapStatus.isOverThreshold = heapInPercent > heapThreshold. value;

heapStatus.isOverMaxThreshold = heapInPercent > heapThreshold.maxValue;

returnheapStatus;

}

解释:

①:currentHeapStatus方法是获取当前的内存状态,主要收集了当前最大内存、已使用的内存、已使用内存的占比、已使用的内存占比是否超过阈值,已使用的内存占比是否超过最大阈值。

②:当前已使用内存是否达到最大阈值,内存使用占比超过 95%(常量值,可配置),如果超过的话,则直接触发。

③:当前已使用内存占比是否触发到阈值,该阈值会根据机型内存来进行变更,具体看 KConstants.getDefaultPercentRation(常量值,可配置)。

④、如果本次记录的内存占比比上次记录的还要大,或是触发到了最大阈值,则记录一下次数。

⑤:记录的次数超过 3 次,则触发。

对于第四点我开始是有点疑虑的,只有内存是在连续 3 次增长的时候才会迭代次数,并且我们的检测是轮训的 5s,如果在增长的次数刚好 2 次,gc 回收又让内存重新回跌,然后次数又会被重置,下次再又增长上来,又要从 0 开始记录次数,这种会不会漏检?但又思考再三,如果内存泄漏的话,内存的趋势肯定是增长状态的,只不过是时间问题,他并不像 crash 检测那样,需要很高的时效性。

2

dump 内存堆栈

Dump hprof是通过虚拟机提供的 API dumpHprofData 实现的,这个过程会 “冻结” 整个应用进程,造成数秒甚至数十秒内用户无法操作,这也是LeakCanary 无法线上部署的最主要原因,如果能将这一过程优化至用户无感知,将会给 OOM 治理带来很大的想象空间。

正如 KOOM 所说的,解决 dump 无感知会是非常大的想象空间,因为他可以部署到线上监控。

KOOM 使用 fork dump 操作,从当前主进程 fork 出一个子进程,由于 linux 的 copy-on-write 机制,子进程和父进程共享的是一块内存,那么我们就可以在子进程中进行 dump 堆栈,不影响主进程的运行。当然其中还是有很多的坑,这里不展开讲,可以查看快手的文章解决 Dump hprof 冻结 app 部分。

HeapDumpTrigger.class

publicvoiddoHeapDump(TriggerReason.DumpReason reason){

// 生成 dump 的 hprof 文件存储路径

KHeapFile.getKHeapFile.buildFiles;

...

// 开始 dump

booleanres = heapDumper.dump(KHeapFile.getKHeapFile.hprof.path);

...

}

heapDumper 实现类有三个,我们只看ForkJvmHeapDumper 类。

ForkJvmHeapDumper

@Override

publicbooleandump(String path){

...

// 适配 Android 11 ,和下面流程差不多

if(Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {

returndumpHprofDataNative(path);

}

...

try{

// ①、挂起主进程并 for 出子进程

intpid = trySuspendVMThenFork;

if(pid == 0) {

// ②、子进程开始 dump hprof

Debug.dumpHprofData(path);

// 结束子进程

exitProcess;

} else{

// ③、恢复挂起的主进程

resumeVM;

// ④、等待子进程的 dump

dumpRes = waitDumping(pid);

}

} catch(IOException e) {

e.printStackTrace;

}

returndumpRes;

}

解释:

① :调用native方法,挂起当前的主进程,并 for 出子进程,该挂起仅仅只是更改ThreadList 变量的线程状态味suspend,主要目的的欺骗子进程的 dump。

② :子进程开始 dump hprof 文件。

③ :恢复挂起的主进程,也是更改ThreadList 变量状态。

④ :等待子进程退出, 看到issue #81 有人对这个等待过程提出了疑虑,作者也进行相应的解答,waitPid 只是暂停线程,而我们 dump 的过程是在HandlerThread 进行的,所以并不影响主线程。

https://github.com/KwaiAppTeam/KOOM/issues/81

dump 出的堆栈已存放到了指定 path 中,接下来只需要继续回到doHeapDump 方法,做接下来的解析操作。

3

分析 hprof 文件

分析的回调有点长,就直接写类和方法好了:

KOOMInternal.onHeapDumped

HeapAnalysisTrigger.startTrack

HeapAnalysisTrigger.trigger

HeapAnalysisTrigger.doAnalysis

HeapAnalyzeService.runAnalysis : 启动一个 IntentService 服务

HeapAnalyzeService.doAnalyze

KHeapAnalyzer.analyze

KHeapAnalyzer.class

publicbooleananalyze{

// 查找泄漏的引用链

Pair<List<ApplicationLeak>, List<LibraryLeak>> leaks = leaksFinder.find;

if(leaks == null) {

returnfalse;

}

//将 gc 引用链写入到 report 文件中

HeapAnalyzeReporter.addGCPath(leaks, leaksFinder.leakReasonTable);

// 标记当前 report 已完成

HeapAnalyzeReporter.done;

returntrue;

}

对于解析,KOOM 做了如下优化:

1、GC root 剪枝,由于我们搜索 Path to GC Root 时,是从 GC Root 自顶向下 BFS,如JavaFrame、MonitorUsed等此类 GC Root 可以直接剪枝。

2、基本类型、基本类型数组不搜索、不解析。

3、同类对象超过阈值时不再搜索。

4、增加预处理,缓存每个类的所有递归 super class,减少重复计算。

5、将object ID的类型从long修改为int,Android虚拟机的object ID大小只有32位,目前shark里使用的都是long来存储的,OOM时百万级对象的情况下,可以节省10M内存。

总结

KOOM 将内存泄漏做到线上监控,已经是市面上几款内存泄漏框架中的一种创新了。

参考文档:

快手客户端稳定性体系建设

KOOM README

https://github.com/KwaiAppTeam/KOOM/blob/master/README.zh-CN.md

最后推荐一下我做的网站,玩Android: wanandroid.com,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏快手秒刷播放网址

5年Android开发面试问什么?

Android开发太难了:Java Lambda ≠ Android Lambda

Android 中看似高大上的字节码修改,这样学就对了!

如果你想要跟大家分享你的文章,欢迎投稿~

┏(^0^)┛明天见!

代刷网地址

留言评论

暂无留言