Android Java进程memleak自动化检测与堆转储方法
前言
由于前一篇文章讲了如何在Java应用集群中高效找出内存泄漏进程的方法,现在开始分享Java进程memleak自动化检测与堆转储方法。
在Java进程集群中,某一个或几个Java进程内存泄漏场景经常是很隐蔽的,这可能需要一系列的进程间业务动作才能暴露出来。同样,在某一个存在内存泄漏的Java进程内部,如果直接研究代码是否存在内存泄漏,这对理论基础和实际经验要求很高,而在高强度的实际场景测试下却是可以有效的复现内存泄漏的。针对上面两个内存泄漏检测方面的困难问题,上一篇文章已经讲了第一个问题的解决办法,现在可以将第二个问题的解决办法。
目录
测试条件
-
监控进程设置
要对监控进程执行堆转储操作,需要在应用源码开启debuggable开关。
+++ b/app/build.gradle
@@ -15,6 +15,7 @@ android {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ debuggable true
}
-
关闭系统Selinux
因权限问题,dumphead操作需要关闭selinux,关闭命令为
setenforce 0
测试方法
通过设置随机种子值进行monkey测试,测试命令样本如下:
monkey -s 123456 --throttle 500 --ignore-crashes --ignore-timeouts --monitor-native-crashes -v -v -v 5000000 &
内存监控捕获办法
-
监控方式
为了监控发现内存泄漏点,我们需要获取的重要信息是发生内存泄漏时,内存泄漏之前的内存快照和内存泄漏之后的内存快照,通过这两份内存快照我们即可定位内存泄漏点。如下示意图所示:
通过对内存泄漏捕获时机确认的建模,可以知道红色曲线表示内存总量上升量超过阈值,此时需要进行内存快照,而当绿色曲线表示内存总量下降量超过阈值时,此时也需要进行内存快照。
-
测试命令
Process=com.****.yeqishi.memleak; lastsize=0; threshod=10000;n=1; while [ $n -lt 300 ];do bigdump=0;smalldump=0; eval $(dumpsys meminfo $Process | grep TOTAL | busybox awk '{printf ("m=%s",$2)}'); if [ $m -gt $lastsize+$threshod ]; then bigdump=1; fi; if [ $m+$threshod -lt $lastsize ];then smalldump=1; fi; date; echo "mem=$m lastsize=$lastsize big=$bigdump small=$smalldump n=$n"; if [ $bigdump == 1 ] || [ $smalldump == 1 ];then am dumpheap $Process /sdcard/dpmi_launcher_$n.hprof; lastsize=$m; ((n=n+1)); fi; sleep 10; done > /sdcard/dpmi-info1 &
Process=com.****.yeqishi.memleak; lastsize=0; threshod=10000;n=1; while [ $n -lt 300 ];do bigdump=0;smalldump=0; eval $(dumpsys meminfo $Process | grep TOTAL | busybox awk '{printf ("m=%s",$2)}'); if [ $m -gt $lastsize+$threshod ]; then bigdump=1; fi; if [ $m+$threshod -lt $lastsize ];then smalldump=1; fi; date; echo "mem=$m lastsize=$lastsize big=$bigdump small=$smalldump n=$n"; if [ $bigdump == 1 ] || [ $smalldump == 1 ];then am dumpheap $Process /sdcard/dpmi_launcher_$n.hprof; lastsize=$m; ((n=n+1)); fi; sleep 10; done > /sdcard/dpmi-info1 &
-
命令说明
Process:指定监控的进程名称
lastsize:最近一次检测到的满足阈值触发条件时的进程堆大小
threshod:阈值大小,(当进程堆内存变化超过阈值时会进行堆转储操作)
上诉命令会每隔10s查询一次监控进程的堆大小,当堆大小与lastsize相比变化超过threshod的值时,触发执行对监控进程的堆转储操作。(由于堆转储生成的文件较大,30M+,所以总监控次数控制在300)
数据查看与抽取
-
查看机器测试文件
自动化堆转储操作命令会将测试过程信息输出到/sdcard/dpmi开头的文件中,通过查看文件可以知道触发动作次数。如下:
通过打印dpmi-info1文件,可以知道测试过程数据,如进程堆内存使用和触发动作次数。
当怀疑监控进程堆内存发生泄漏时,可以导出/sdcard/dpmi-info1文件,执行以下命令导出监控进程堆内存历史数据到表格文件中,同时利用表格软件生成数据曲线,确认内存泄漏或者内存使用情况。
cat C8W7KPSQB3.dpmi-info1 | grep mem | awk -F "[= ]" '{print $2}' > C8W7KPSQB3.csv
通过以下命令确认内存变化超过阈值时触发动作的发生时间
cat C8W7KPSQB3.dpmi-info1 | tr "\n" "," | sed 's#Mon #\nMon #g' |sed 's#Tue #\nTue #g' | grep =1 > a
根据时间或者动作次数确认堆转储hprof 文件,上诉信息可选择第三、四次触发动作时的hprof文件。
Hprof文件分析
-
hprof格式转化
命令导出的gprof文件需要格式转换,转换工具为sdk/platform-tools/hprof-conv
-
hprof数据分析
MAT分析办法参考:https://blog.****.net/tabactivity/article/details/73773916
-
定位泄漏原因
上诉对比图可以看出第三、四次堆转储触发时进程对象变化对比,个人建议从自己可知的大对象(与根对象引用深度浅的目标对象)确认代码泄漏点。