1。 查看当前手机HEAP size 设定
adb shell getprop | grep heap
[dalvik.vm.heapgrowthlimit]: [192m]
[dalvik.vm.heapsize]: [512m]
可以查看到heapsize 的大小
2. 使用procrank 跟踪某个进程的内存
App:Application
VSS Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PSS Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
USS Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)
procrank 命令可以获得当前系统中各进程的内存使用快照,这里有PSS,USS,VSS,RSS。
我们一般观察Uss来反映一个Process的内存使用情况,Uss 的大小代表了只属于本进程正在使
用的内存大小,这些内存在此Process被杀掉之后,会被完整的回收掉,
Vss和Rss对查看某一Process自身内存状况没有什么价值,因为他们包含了共享库的内存使
用,而往往共享库的资源占用比重是很大的,这样就稀释了对Process自身创建内存波动。
而Pss是按照比例将共享内存分割,某一Process对共享内存的占用情况。
procrank 的代码在 /system/extras/procrank,,在模拟器或者设备上的运行文件位/system/xbin
在adb shell之后,我们运行procrank
adb shell procrank
我们还可以使用脚本配合procrank跟踪内存变化
使用procrank来跟踪某进程的使用哪个情况我们常常借助与脚本。这样就可以查看某一段时间
的内存变化。
如创建一个文件:trackmem.sh chmod 775 trackmem.sh
内容如下:
#!/bin/bash
while true; do
adb shell procrank | grep "com.baidu.BaiduReader"
sleep 1
done
运行该脚本:
./trackmem.sh
这个脚本的用途是每1秒钟让系统输出一次BaiduReader的内存使用状况
3. 查看某个进程的内存信息
例如 要查看 com.android.systemui 的内存使用情况
adb shell dumpsys meminfo com.android.systemui
** MEMINFO in pid 1179 [com.android.systemui] **
Pss Private Private Swapped Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 7207 7184 0 260 8192 8043 92
Dalvik Heap 69196 69144 0 1028 72248 69354 2894
Dalvik Other 2587 2504 0 1012
Stack 156 156 0 16
Ashmem 2 0 0 0
Other dev 5 0 4 0
.so mmap 920 204 20 2688
.apk mmap 1516 0 4 0
.ttf mmap 4 0 0 0
.dex mmap 1272 68 652 136
Other mmap 37 4 28 0
Unknown 108 108 0 8
TOTAL 83010 79372 708 5148 80440 77397 2986
Objects
Views: 414 ViewRootImpl: 1
AppContexts: 9 Activities: 0
Assets: 6 AssetManagers: 6
Local Binders: 76 Proxy Binders: 48
Death Recipients: 3
OpenSSL Sockets: 0
SQL
MEMORY_USED: 0
PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0
可以查看Native Heap 和 Davilik Heap 使用情况
4。查看GC log
先查看systemui 进程id
adb shell ps | grep systemui
u0_a64 910 195 939612 75312 ffffffff 400aa560 S com.android.systemui
查看 log
adb logcat -v time | grep "910): GC_"
01-01 01:20:22.040 D/dalvikvm( 910): GC_EXPLICIT freed 865K (12243), 51% free 29409K/59320K, paused 6ms+7ms, total 56ms
01-01 01:20:26.497 D/dalvikvm( 910): GC_FOR_ALLOC freed 316K (4580), 44% free 33467K/59320K, paused 49ms, total 49ms
adb logcat -v time -s dalvikvm-heap
01-01 01:21:26.511 I/dalvikvm-heap( 910): Grow heap (frag case) to 64.409MB for 16588816-byte allocatio
1、GC_FOR_ALLOC:发生在堆被占满不能进行内存分配时,在分配新对象之前必须进行内存回收。
2、GC_CONCURRENT:发生在(可能是部分的)垃圾可供回收时,通常有很多对象可以回收。
3、GC_EXPLICIT:显式调用System.gc()产生的垃圾收集。
freed 865K 表示 释放865k 内存, 51% free 29409K/59320K 表示heapsize 是 59M 已用29M 有51%的 free
5 .use MAT 分析 dump hprof 文件 简单介绍一下ShallowHeap和RetainedHeap。
Shallow size就是对象本身占用内存的大小,不包含对其他对象的引用,也就是对象头加成员
变量(不是成员变量的值)的总和。在32位系统上,对象头占用8字节,int占用4字节,不管成
员变量(对象或数组)是否引用了其他对象(实例)或者赋值为null它始终占用4字节。
Retained size是该对象自己的shallow size,加上从该对象能直接或间接访问到对象的shallow
size之和。换句话说,retained size是该对象被GC之后所能回收到内存的总和。
使用MAT
http://blog.csdn.net/yangchuxi/article/details/6780097
http://blog.csdn.net/amy0428/article/details/7057282
http://blog.csdn.net/aaa2832/article/details/19419679
用MAT分析内存泄露
1. 利用Android DDMS dump 当前堆栈的hprof 文件
2. 利用hprof-conv 转换dump出来的hprof 文件
3. 用MAT 打开分析
先点击Histogram 查看 当前堆 里面所有的java object 对象。 可进行排序。查看Retained size 占很高的对象。或者Objects 数量很多的对象。是否是正常行为。
在Histogram 里面进行 过滤 查找。 查找本app 里面的Activity, Fragment 数量个数是否正确。
一种比较容易发生内存泄露的方式。就是由于有一些Activity 或者 Fragment 的内部类 的引用。造成Activity 和 fragment 无法被系统回收
(注意: Java 种 内部类 会 自动持有 外部类的引用。 这样可以调用外部类的成员变量)
所以一般Activity 或者 Fragment 中有内部类的。需要注意内部类的生命周期。比如: BroaderCastReceiver , Timer , Thread, Hanlder, Animator的 Listener 等等。
需要特别注意这些对象的生命周期 是否超过了Activity 或者 Fragment 的生命周期
6. 利用CashHandler 自动dump hprof 文件。
为了便于在OOM发生的时候 能够Dump Hprof 文件。 提供分析。可以设置全局的CashHandler 并判断是否OOM ,如果是OOM 则利用
Debug类dump hprof 文件
dumpHprofData(String fileName)
private static finalString OOM= "java.lang.OutOfMemoryError";
if(isOOM(arg1)) {
SimpleDateFormat sDateFormat =
newSimpleDateFormat("MM_dd_hh_mm_ss");String date = sDateFormat.format(newDate());File dumpfile = newFile(logdir + File.separator+ "oom"+ date
+ ".hprof");Debug.dumpHprofData(dumpfile.getAbsolutePath());}
public static booleanisOOM(Throwable throwable){
if(OOM.equalsIgnoreCase(throwable.getClass().getName())){
return true;}else{
Throwable cause = throwable.getCause();if(cause != null){
returnisOOM(cause);}
return false;}
}