[Android算法] android OutOfMemory时抓取heap快照
首先说一下,在程序没有崩溃的时候如何抓取heap快照。
这个大家应该都知道,在ddms中自带此功能。
见上图
首先我们选中一个进程,然后点击 Update Heap按钮
如果想取出快照详细分析,我们可以点击 Dump HPROF File按钮
01 | hprof-conv '/home/su1216/data.hprof' '/home/su1216/data_ok.hprof' |
这时MAT能直接打开data_ok.hprof文件。
如果想要OOM时的内存快照该怎么办,我们总不能紧盯着手机的同时再盯着电脑,OOM出现的瞬间抓取内存快照,这显然是不现实的。
如果OOM并不经常复现,那么我们会错过很多修改bug的机会,浪费很多时间。
下面给大家一种抓取OOM时的heap快照的方法
由于OOM时的heap快照较大,所以抓取的内存快照我选择保存到sd卡中,因此要有写入外部存储的权限
01 | < uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" /> |
然后我们需要实现UncaughtExceptionHandler这个接口
记得要设置未捕获异常的Handler,设置为自己。
当出现了异常的时候,uncaughtException方法会被调用,所以如果我们可以在这里抓取内存快照。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | import java.lang.Thread.UncaughtExceptionHandler; import android.os.Debug; import android.os.Environment; import android.util.Log; public class CrashHandler implements UncaughtExceptionHandler { public static final String TAG = "CrashHandler" ; private Thread.UncaughtExceptionHandler mDefaultHandler; private static final String OOM = "java.lang.OutOfMemoryError" ; private static final String HPROF_FILE_PATH = Environment.getExternalStorageDirectory().getPath() + "/data.hprof" ; private static CrashHandler sCrashHandler; private CrashHandler() {} public synchronized static CrashHandler getInstance() { if (sCrashHandler == null ) { sCrashHandler = new CrashHandler(); } return sCrashHandler; } public void init() { mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler( this ); } public static boolean isOOM(Throwable throwable){ Log.d(TAG, "getName:" + throwable.getClass().getName()); if (OOM.equals(throwable.getClass().getName())){ return true ; } else { Throwable cause = throwable.getCause(); if (cause != null ){ return isOOM(cause); } return false ; } } public void uncaughtException(Thread thread, Throwable throwable) { if (isOOM(throwable)){ try { Debug.dumpHprofData(HPROF_FILE_PATH); } catch (Exception e) { Log.e(TAG, "couldn’t dump hprof" , e); } } if (mDefaultHandler != null ) { mDefaultHandler.uncaughtException(thread, throwable); } else { android.os.Process.killProcess(android.os.Process.myPid()); System.exit( 1 ); } } } |
最关键的代码是这句
01 | Debug.dumpHprofData(<span style= "line-height: 18px;" >HPROF_FILE_PATH</span>); |
使得我们可以自己控制抓取heap快照的时机
OutOfMemoryError是系统级别的错误,所以一般情况下不该捕获它。
万一有人捕获了,并且重新抛出了一个调用了initCause方法的异常,我们也应该截获它,然后修正bug,而不是掩藏它。
我们在这里只是需要抓取内存快照,干完活之后要记得把throwable交给系统来处理
01 | mDefaultHandler.uncaughtException(thread, throwable); |
当然,我们在这个地方实际上也可以屏蔽掉force close对话框,很神奇吧。。。
结尾顺便说一下,如何查看android对应用的内存限制
每款手机对应用的限制都是不一样的,毕竟硬件不同,我们可以使用如下方式来查看单独的应用可使用的最大内存:
adb shell getprop | grep heap
[dalvik.vm.heapgrowthlimit]: [64m]
[dalvik.vm.heapsize]: [256m]
[dalvik.vm.heapstartsize]: [8m]
输入命令之后回查到上述几个结果
[dalvik.vm.heapstartsize]: [8m]:给进程分配的起始heap=8m
[dalvik.vm.heapgrowthlimit]: [64m]:进程最大可分配到64m
[dalvik.vm.heapsize]: [256m]:单个虚拟机可分配的最大内存=256m
更改上述参数可以在build.prop修改
build.prop在system下,pull出来修改后再push回去,reboot即可
我们可以根据这个来估计heap的大小,检查sd卡剩余空间是否够用。