Android 内存分析总结
一直没有写博客的习惯,最近觉得年纪貌似有点大了,不像以前记忆这么好,想找个方式梳理一下知识,刚好最近在ITeye上看之前一起工作过的一个大哥写的一些关于状态机的一些东西,就萌生了也写一写,记录一下的想法,第一篇就先说一说Android内存方面的一些事情吧。
很多人在进行Android开发的过程中经常会碰到内存方面的问题,比如说内存溢出,应用莫名奇妙的Crash,有时也会被客户抱怨说在第三方的应用管理软件中内存开销非常的大,这时候就需要做一些内存分析和优化的事情,本文主要来讨论内存分析的事情。
很多人一看到OOM就觉得是内存有泄漏,其实OOM并不总是由于内存泄漏所致,我个人认为,所谓泄漏是指内存无法被收回,就是说有一段逻辑会导致一些内存无法被收回,如果反复执行这段逻辑,内存会持续的增长,最后导致OOM,很多时候,OOM是我们不恰当的使用内存(尤其是图片的处理)所致,比如,在较短的时间加载了很多规格较大的图片,这方面的问题,本文也不做深入讨论(其实Google Guide有很深入的讨论,可参考 Displaying Bitmaps Efficiently 一文),我们重点来说说如何分析内存。
要分析内存,我们首先要找到衡量内存大小的一个标准,就是到底使用了多少内存,很不幸,由于Linux的内存共享机制,每个应用使用了多少内存却是笔糊涂帐,通常我们有下面几种方式来衡量:
- VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
- RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)
- PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
- USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)
一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS,一般我们会用PSS来作为内存大小的衡量值,我曾经饭编译过一些内存管理软件,显示使用的内存其实就是PSS
我们可以通过命令
adb shell procrank来查看内存状况
PID Vss Rss Pss Uss cmdline
494 57372K 57232K 33128K 30336K system_server
585 46924K 46708K 25082K 23464K com.android.systemui
通过
adb shell top来查看内存(VSS RSS)和CPU使用状况:
PID PR CPU% S #THR VSS RSS PCY UID Name
2735 0 0% R 1 1212K 504K shell top
950 0 0% S 18 661416K 25736K fg u0_a19 android.process.media
adb shell dumpsys meminfo <package_name>
下面这个是guide里面gmail的一个dump,能够比较清晰全面的看到当前内存的使用状况,具体的含义guide里面有详细的解释,这里想说的是Dalvik Heap 和我们下面要说到的Heap moniter 中的heap size和GC 日志中 GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, 中的heap status是一样的(实际测试也是一样的),
通常我们说一个应用最多只能分配多少多少的内存实际上指的也是Dalvik Heap(在system/build.prop中定义),当应用分配的内存要超过这个定义时,就会报OOM,这个也就解释了说为什么我们通过第三方应用开我们的应用已经使用了50,甚至 60M的内存,我们的heap size只有32M,但却没有OOM,因为第三方显示的是PSS,是包含了共享内存的(这也是科学的),通过这个我们可以清楚的看到我们的内存状况是怎样的,这样内存优化也会比较有针对性。
Pss Pss Shared Private Shared Private Heap Heap Heap Total Clean Dirty Dirty Clean Clean Size Alloc Free ------ ------ ------ ------ ------ ------ ------ ------ ------ Native Heap 0 0 0 0 0 0 7800 7637(6) 126 Dalvik Heap 5110(3) 0 4136 4988(3) 0 0 9168 8958(6) 210 Dalvik Other 2850 0 2684 2772 0 0 Stack 36 0 8 36 0 0 Cursor 136 0 0 136 0 0 Ashmem 12 0 28 0 0 0 Other dev 380 0 24 376 0 4 .so mmap 5443(5) 1996 2584 2664(5) 5788 1996(5) .apk mmap 235 32 0 0 1252 32 .ttf mmap 36 12 0 0 88 12 .dex mmap 3019(5) 2148 0 0 8936 2148(5) Other mmap 107 0 8 8 324 68 Unknown 6994(4) 0 252 6992(4) 0 0 TOTAL 24358(1) 4188 9724 17972(2)16388 4260(2)16968 16595 336 Objects Views: 426 ViewRootImpl: 3(8) AppContexts: 6(7) Activities: 2(7) Assets: 2 AssetManagers: 2 Local Binders: 64 Proxy Binders: 34 Death Recipients: 0 OpenSSL Sockets: 1 SQL MEMORY_USED: 1739 PAGECACHE_OVERFLOW: 1164 MALLOC_SIZE: 62
通过DDMS 自带的Heap moniter也是不错的选择:
另外,有一个非常著名的Eclipse插件,叫MAT(Memory Analyzer Tool),非常的好用,尤其是它的Memory leak suspects,当你的内存一直持续的增长,增长到很大的一个值时,用MAT的Memory leak suspects,几乎 100%会指出你leak的地方在哪:
Allocation Tracker:
这个工具可以让我们在一个时间段跟踪内存的分配状况,在DDMS的Allocation Tracker:点击Start Tracking,点击几下你的应用后,再点击Get Allocations 就可以看到:
图片右下方的 at XXX 一大堆的就是一层层的调用关系,可以清晰的看到到底是谁申请了内存,申请了多少。
我们甚至还可以在代码中将将瞬态内存打印出来:
public static void printMeminfo(String where)
{
ActivityManager mgr = (ActivityManager) mContext
.getSystemService(Context.ACTIVITY_SERVICE);
int id = android.os.Process.myPid();
int[] ids = { id };
MemoryInfo[] minfo = mgr.getProcessMemoryInfo(ids);
Log.d("meminfo", where + " dalvikPrivateDirty:"
+ minfo[0].dalvikPrivateDirty + " dalvikPss:"
+ minfo[0].dalvikPss + " TotalPss: " + minfo[0].getTotalPss()
+ " TotalPrivateDirty: " + minfo[0].getTotalPrivateDirty());
}
我们在日志中经常看到有内存回收的打印:
D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, external 4703K/5261K, paused 2ms+2ms
其含义为:
D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <External_memory_stats>, <Pause_time>
其中GC Reason 有如下几个,我看到网上的一些文章,对GC Reason说的并不正确,这里我把guide中的原文一并贴上:
GC_CONCURRENT
GC_CONCURRENT能回收较多的内存(
Heap size都快满了,用了不少的内存,应该有一部分不需要了),
12-12 08:01:10.420: D/dalvikvm(585): GC_CONCURRENT freed 11144K, 52% free 10641K/21936K, paused 3ms+4ms, total 38ms
12-12 08:01:10.890: D/dalvikvm(1336): GC_CONCURRENT freed 266K, 7% free 6321K/6736K, paused 8ms+3ms, total 36ms
12-12 08:01:10.910: D/dalvikvm(585): GC_CONCURRENT freed 1451K, 51% free 10881K/21936K, paused 3ms+3ms, total 34ms
12-12 08:01:11.000: D/dalvikvm(585): GC_CONCURRENT freed 2127K, 52% free 10616K/21936K, paused 2ms+3ms, total 22ms
12-12 08:01:11.050: D/dalvikvm(585): GC_CONCURRENT freed 1431K, 51% free 10883K/21936K, paused 2ms+2ms, total 20ms
12-12 08:01:12.900: D/dalvikvm(843): GC_FOR_ALLOC freed 3323K, 22% free 14150K/18120K, paused 25ms, total 25ms
12-12 08:01:13.630: D/dalvikvm(680): GC_CONCURRENT freed 156K, 6% free 5658K/5960K, paused 1ms+4ms, total 26ms
12-12 08:01:13.720: D/dalvikvm(680): GC_CONCURRENT freed 356K, 8% free 5812K/6312K, paused 3ms+2ms, total 19ms
GC_FOR_MALLOC
GC_FOR_MALLOC
我们的heap size会增大
12-12 07:04:10.810: D/dalvikvm(1424): GC_CONCURRENT freed <1K, 6% free 7332K/7768K, paused 3ms+1ms, total 21ms
12-12 07:04:10.850: D/dalvikvm(1424): GC_FOR_ALLOC freed <1K, 6% free 7332K/7768K, paused 22ms, total 22ms
12-12 07:04:10.870: D/dalvikvm(1424): GC_FOR_ALLOC freed 0K, 4% free 10655K/11092K, paused 19ms, total 19ms
12-12 07:04:13.060: D/dalvikvm(1972): GC_CONCURRENT freed 3480K, 20% free 15365K/19004K, paused 2ms+3ms, total 54ms
12-12 07:04:15.470: D/dalvikvm(1972): GC_FOR_ALLOC freed 1763K, 20% free 15336K/19004K, paused 44ms, total 44ms
12-12 07:04:16.120: D/dalvikvm(1972): GC_FOR_ALLOC freed 3K, 20% free 15392K/19064K, paused 25ms, total 25ms
12-12 07:04:17.740: D/dalvikvm(585): GC_FOR_ALLOC freed 3557K, 20% free 17650K/21936K, paused 28ms, total 29ms
GC_HPROF_DUMP_HEAP
GC_EXPLICIT
gc()
(which you should avoid calling and instead trust the garbage collector to run when needed).GC_EXTERNAL_ALLOC
上一篇: jQuery操作表格的插件datatables如何应用
下一篇: Java习题_1