用LeakCanary和Eagle工具定位和分析内存泄漏问题

背景介绍

在日常的开发、测试过程中,有时会遇到 OutOfMemoryErrorJava堆溢出了,这表明程序有严重的问题,如果不解决此类问题,使用App过程中会因为内存占用过高而被系统强行收回堆空间App异常退出,可见内存占用和内存泄漏问题十分影响用户的使用。因此我们需要找到造成OutOfMemoryError原因。一般有两种情况:

1、内存泄漏,对象已经死了,无法通过垃圾收集器进行自动回收,通过找出泄漏的代码位置和原因,以便确定解决方案;

2、内存溢出,内存中的对象都还必须存活着,这说明Java堆分配空间不足,检查堆设置大小,检查代码是否存在对象生命周期太长、持有状态时间过长的情况。

以上是处理Java堆问题的思路,具体是怎么进行分析,这里介绍如何使用LeakCanary工具和Eagle工具定位和分析内存泄漏的问题。

LeakCanary是一个检测内存泄漏的开源类库,Eagle工具是在Eclipse Memory Analyzer tool(MAT)工具上的二次开发,新增对比操作前后两个堆快照改变的对象数。

LeakCanary的工作原理

1.RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。

2.在后台线程检查引用是否被清除,如果没有,调用GC。

3.如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。

4.在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。

5.通过唯一的reference key, HeapAnalyzer找到KeyedWeakReference,定位内存泄漏。

6.HeapAnalyzer计算到GC root的最短强引用路径,并确定是否是泄漏。如果是的话,建立导致泄漏的引用链。

7.引用链传递到APP进程中的 DisplayLeakService,并以通知的形式展示出来。


Eagle安装说明

若非第一次安装MAT插件,先卸载MAT

安装经过二次开发后的MAT插件

1)进入help->install New Software->add

2)点击Archive,将xxx.zip选中,点击ok进行安装

3)待MAT工具安装完成重启eclipse

安装Eagle插件

4)将Eagle_xxx.jar放入eclipse的dropins目录,然后重启eclipse,即安装成功

验证是否安装成功操作如下,效果如下图

5)进入Memory Analysis打开一个hprof文件查看主菜单中是否有Eagle菜单项,以及对应的子菜单项Compare、Bitmap

6)打开Eclipse->Window->Show View->others中选择Eagle Views->Show Bitmap View

常规手工检查内存泄漏的操作是:

1)找到内存异常时的复现场景。

2)在内存泄漏开始时抓取一个hprof 文件,在内存泄漏很厉害时,app濒临崩溃时再抓取一个hprof 文件。

3)用Eagle工具的Compare功能, 对比看前后这两个dump的hprof文件,根据RetainedHeap项按照从大到小排序,就很容易看出来哪一块存在内存泄漏。有的时候能直接看出来多了一块,那么就从那一块入手进行分析,能比较快得到结果。

4)查找这个对象到 GC roots的最短强引用路径。

5)确定引用路径中的哪个引用是不该有的,然后对照代码修复问题。


推荐使用内存泄漏自动检测工具LeakCanary,步骤如下图:

分享一个结合LeakCanary和Eagle工具定位分析内存泄漏的实例

LeakCanary定位发现的问题信息如下:

使用Eagle工具进一步分析的步骤:

1、初始状态获取堆快照start.hprof


2、至待测状态获取堆快照end.hprof


3、使用Ealge工具对比两个快照

   (1)进入Memory Analysis,打开end.hprof->open query browser,选择Eagle->Compare


   (2)-s选择初始快照start.hprof,-f选择类名过滤词,默认值为baidu


(3) 点击finish展示过滤结果表如下:


进一步详细分析是Context使用不当

修复前:                                                                                         修复后:   

将activity的context改为applicationContext,applicationContext生命周期更长,原因是引用了context导致activity结束的时候context不能释放,从而引发内存泄漏。

 

如果你看的意犹未尽,如果你想随时随地充实自己,请扫描以下二维码,关注我们的公众账号,可以获取更多技术类干货,还有精彩活动与你分享~

大咖招募
欢迎App研发/测试方面的大牛来投稿,开设专栏。我们提供丰厚的稿酬,预约个人专访,帮助建立个人技术品牌!
立即投稿

我要评论

字数不能超过140字,谢谢!
提交

评论({{allComments.length}})

{{comment.create_time.substr(0,16)}}

显示所有评论
复制成功!