leakcanary探索
来源:互联网 发布:excel如何保护数据 编辑:程序博客网 时间:2024/05/22 00:51
leakcanary探索
- leakcanary探索
- 什么是leakcanary
- 粗略工作流程
- 内存泄露判定实现
- 系统回收
- 对象引用可达性判断
- heapDump的分析
- 内存泄露判定实现
Android应用开发中,OutOfMemory是一个很让人头疼的问题,特别是迭代时间较长的项目中,问题定位较难。在gitHub上发现一个开源项目——leakcanary,可以辅助检测并且定位内存泄漏。抽个时间研究一下它的实现。
什么是leakcanary
A memory leak detection library for Android and Java.
leakcanary 包装了简单易用的接口,只需简单若干行代码,就可以开始工作。通过监控对象的回收情况,定位内存泄漏并自动执行HeapDump,自动分析hrpof文件找出泄露的引用链,在通知栏自动提示用户。
项目内置默认实现了ActivityRefWatcher,用于监控Activity,但只能用于Android 4.0及其之上。如果想支持4.0之下的系统,需要定义BaseActivity复写onDestroy来获得统一监控触发点。
理论上,leakcanary可以做到对象级别的内存泄露分析,同样的需要自己实现对应的RefWatcher,在合适的时机进行触发分析。
粗略工作流程
根据上面的流程图,从技术实现上,个人比较关注的是两个点:
- 如何判断一个对象属于内存泄漏
- 如何在hprof文件里面定位到问题并抓取出来
内存泄露判定实现
内存泄漏,指的是脱离了使用场景的对象,系统进行回收的时候无法成功释放资源,导致系列的问题。根据定义,思路大概是这样:
系统回收
GcTrigger DEFAULT = new GcTrigger() { @Override public void runGc() { // Code taken from AOSP FinalizationTest: // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/ // java/lang/ref/FinalizationTester.java // System.gc() does not garbage collect every time. Runtime.gc() is // more likely to perfom a gc. Runtime.getRuntime().gc(); //不确保一定能够触发gc enqueueReferences(); //sleep一定时间(100ms),来确保gc完成 System.runFinalization();//为什么要放到enqueueReferences()后面? }
如我们所知,Java里面的gc拥有各种各样的垃圾收集算法,gc的执行具有很大的不确定性。不管是用System.gc()
还是Runtime.getRuntime().gc()
,都是对jvm的一个建议,引发jvm的内部垃圾算法的加权,无法保证gc一定马上执行。所以即使sleep了一定的时间等待gc,仍有极大的可能误报。
对象引用可达性判断
先让我们来看一下逻辑的执行顺序。
对象引用生成key并记录
public void watch(Object watchedReference, String referenceName) { ...检查... final long watchStartNanoTime = System.nanoTime(); String key = UUID.randomUUID().toString(); retainedKeys.add(key);//直接记录key //使用ReferenceQueue和WeakReference来记录引用被回收 final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, queue); ...异步执行gc...}
注意到这里使用了
ReferenceQueue
,查阅文档得知,弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
不同类型Reference的解释实际的gc和判断过程
void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) { long gcStartNanoTime = System.nanoTime(); long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); removeWeaklyReachableReferences(); if (gone(reference) || debuggerControl.isDebuggerAttached()) { return; } gcTrigger.runGc();//实际执行gc removeWeaklyReachableReferences(); //如果引用对应的key仍然存在,认为是泄漏,执行heapDump if (!gone(reference)) { long startDumpHeap = System.nanoTime(); long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); File heapDumpFile = heapDumper.dumpHeap(); if (heapDumpFile == null) { // Could not dump the heap, abort. return; } long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap); heapdumpListener.analyze(new HeapDump(heapDumpFile, reference.key, reference.name, watchDurationMs, gcDurationMs,heapDumpDurationMs)); }}//简单的key判断private boolean gone(KeyedWeakReference reference) { return !retainedKeys.contains(reference.key);}//凡是存在ReferenceQueue中的,都认为是被回收的,remove之private void removeWeaklyReachableReferences() { // WeakReferences are enqueued as soon as the object to which they point to becomes weakly // reachable. This is before finalization or garbage collection has actually happened. KeyedWeakReference ref; while ((ref = (KeyedWeakReference) queue.poll()) != null) { retainedKeys.remove(ref.key); }}
回顾这个过程,有一个比较明显的问题,就是watch的时候是直接执行了
retainedKeys.add(key)
,意图在后期通过GC来利用ReferenceQueue的enqueue,从而remove(key)
。而在前面的分析中,GC的不确定是很强的,所以存在误报的可能。
heapDump的分析
待续
- leakcanary探索
- LeakCanary
- LeakCanary
- LeakCanary
- LeakCanary
- LeakCanary小记
- LeakCanary Eclipse
- LeakCanary (一)
- LeakCanary (二)
- LeakCanary使用
- LeakCanary使用
- Android LeakCanary
- LeakCanary学习
- LeakCanary使用手册
- 初识LeakCanary
- [源码]LeakCanary
- LeakCanary用法
- LeakCanary使用说明
- LINUXCNC的python模块生成函数
- ORA-01810格式代码出现两次 的解决办法
- andoird 按返回键退出
- 【Android】使用AsyncTask处理耗时任务
- Uva - 679 - Dropping Balls
- leakcanary探索
- 算法基本概念
- hdu 2255 奔小康赚大钱【最大权匹配】
- 【OpenCV】访问图像中每个像素的值
- svn地址变换以后,mac下对策
- “屌丝”元组织
- Android Toolchain与Bionic Libc
- Android 创建项目ActionBar(操作栏)不显示的问题
- putty最佳的的颜色配置