概述
内存泄露,这个应用开发中比较容易出现的问题,由于短时间内难以测出来一般都比较难以发现,只能靠开发者的警觉性来避免,长时间的Monkey测试就成为一个可行的手段。
Monkey测试能给出一个直观的内存变化曲线和日志,GC日志在内存紧张时都会打印出测试进程的内存信息。往往报错信息也会指明是 java.lang.OutOfMemoryError但是报这个异常的地方往往并不是内存泄露的位置,它只是比较倒霉正好申请内存发现不足了。
直接找重点GC日志类似这样:
I art : Starting a blocking GC Alloc
I art : Starting a blocking GC Alloc
I art : Waiting for a blocking GC Alloc
I art : Alloc sticky concurrent mark sweep GC freed 5223(480KB) AllocSpace objects, 5(100KB) LOS objects, 1% free, 189MB/192MB, paused 2.153ms total 35.644ms
I art : WaitForGcToComplete blocked for 21.733ms for cause Alloc
I art : Starting a blocking GC Alloc
相信网上解释这几句日志的博客已经很多了,就是现在已用内存189M上限是192M,应用下一次申请超过剩余操作限制了就OOM了。
首先确认信息是否符合系统设定
查看系统设置单个进程的内存上限
adb shell
root@125:/ # getprop|grep heapgrowthlimit
[dalvik.vm.heapgrowthlimit]: [192m]
没错就是配置的192M,你的应用消耗的太多了,肯定是有内存泄露了的。
那么怎么查找问题呢,通过这个日志直接分析不到问题,只能根据日志看出应用的大致操作流程,不过Monkey几万次估计所有逻辑都走到了,还是对整个应用内容的使用情况进行检查。
Android上应用开发常见的内存泄露的类型可以看看大神写的http://blog.csdn.net/u010687392/article/details/49909477
其实自从Bitmap的内存分配到java层之后这一类很容易搞定了,用弱引用的缓存直接就安了,这个直接全代码搜索一下挨个改就行了;
其他的同样显性的BraodcastReceiver,ContentObserver,File,Cursor,Stream这一类全代码搜索改就行了;
剩下的比较难找的就是Activity的泄露同时也是应用主要的内存泄露问题,这个如果对代码很熟悉也可以对代码中所有的context、handler等搜索处理,但是如果封装层次比较深模块代码量比较大的情况下,会花费较长的时间。
首先可以用adb shell dumpsys meminfo package name看看当前进程的内存使用情况
Applications Memory Usage (kB):
Uptime: 774504 Realtime: 774504
** MEMINFO in pid 5592 [package namer] **
Pss Private Private Swapped Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 9076 8980 0 0 12776 12483 292
Dalvik Heap 12276 12200 0 0 14499 13738 761
Dalvik Other 956 956 0 0
Stack 328 328 0 0
Gfx dev 3926 3056 0 0
Other dev 5 0 4 0
.so mmap 2320 464 60 0
.apk mmap 136 0 16 0
.ttf mmap 346 0 204 0
.dex mmap 2524 0 2520 0
.oat mmap 4493 0 1756 0
.art mmap 2465 1196 628 0
Other mmap 51 4 40 0
EGL mtrack 28224 28224 0 0
Unknown 233 232 0 0
TOTAL 67359 55640 5228 0 27275 26221 1053
Objects
Views: 271 ViewRootImpl: 1
AppContexts: 7 Activities: 1
Assets: 3 AssetManagers: 3
Local Binders: 22 Proxy Binders: 32
Parcel memory: 8 Parcel count: 34
Death Recipients: 0 OpenSSL Sockets: 0
SQL
MEMORY_USED: 1206
PAGECACHE_OVERFLOW: 946 MALLOC_SIZE: 62
DATABASES
pgsz dbsz Lookaside(b) cache Dbname
1 1673 281 834/125/25 /data/data/package name/databases/sqlit.db
如果此时这里的Activities与目前应用的运行状态不一致,比如目前退出了应该为零但是这里还有数值,很显然就是有Context泄露了(其实这里还能看到很多其他内存数据),这时候就可以使用MAT工具进行分析。
先生成一个hprof文件,然后用MAT工具打开,直接在总览界面选择
Histogram: Lists number of instances per class
这时会将所有当前对象及其数量Heap大小一起列出来,非常的多,可以直接输入Activity或者其他关键字进行搜索。
如果其中的Objects数目与当前应用运行情况不一致就是有问题的地方。
在此条记录上右键选择Merge Shortest Paths to GC Roots --> with all references就可以列出当前对象到GC Root之间的最短引用链,找到并打断这个引用关系就是我们工作
这个栗子由于项目关系就不能贴上具体代码和截图了,最终是在一个单例中发现了一个HashMap引用了一系列的Context在里面。
好了,分析完毕,为了最小的改动,直接将HashMap修改成WeakHashMap,这个东西是在检测到某个key值没有被引用的情况下会回收掉entry,最后检查一下它的get方法如果为空会不会有问题,看一遍没有问题。
问题解决,虽然看起来分析的很愉快,但是在项目的关键时期大半夜看这个还是很伤的,开发者还是需要在写代码的时候就注意内存泄露的问题,尤其是Context的使用,没事多用Application的Context,实在不行也要注意及时主动置空和弱引用。
最后
以上就是粗犷冰棍为你收集整理的应用内存泄露问题分析实例的全部内容,希望文章能够帮你解决应用内存泄露问题分析实例所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复