Android系统一般会限制一个App进程的内存使用大小,当内存使用超过这个大小的时候,就会发生内存溢出,造成手机卡顿、崩溃。内存使用不当的因素有很多,比如常见的内存泄露、内存抖动、Bitmap的使用问题(使用第三方框架可以避免)等等。
在Android系统中,虚拟机有垃圾自动回收机制,不定期地对无用的对象进行回收清理,回收一个对象的主要依据是当前对象有没有被强引用,如果一个本该被回收的对象,但是因为某种原因被强引用了,就会造成这个对象无法被回收,这就是内存泄露。如果这种对象越积越多,会造成手机卡顿,甚至崩溃。
1、LeakCanary
LeakCanary是一个检测内存泄露的第三方框架,集成到项目中,使用起来非常简单,只要检测到App有内存泄露发生,就会吐司提示,并且从界面中可以清楚地看到泄露对象的引用路径,如下图:
2、Memory Monitor
Android Studio自带的内存检测工具,通过Memory Monitor可以方便地观察到App的内存使用情况,看看它的界面
上面是内存检测的一个界面截图,其中红色箭头标识的1-2-3分别代表:
1.手动触发GC进行内存回收
2.Dump Java Heap获取当前的堆栈信息,生成一个.hprof文件,通过这个文件可以看到VM在当前时刻包含的类的实例和数组对象,通常可以进行内存泄露的分析。
3.追踪内存使用情况,第一次点击此按钮代表开始追踪,一段时间后再点击代表结束追踪,可以记录在这段时间区间内各个线程中各个方法在内存中的分配情况,一般用于追踪某项操作之后的内存分配,调整相关的方法调用来优化app性能与内存使用。
2.1、用Dump Java Heap分析内存泄露
用Dump Java Heap生成的hprof文件,可以看到VM堆栈里面的对象分配情况,所以可以通过这种方法检测发生内存泄露的地方,通常操作步骤如下:
下面通过实例来演示分析内存泄露的步骤,一顿操作猛如虎,最后生成了一个.hprof文件如下
其中:
Alloc Count:生成对象的数量;
Shallow Size:这些对象所占用的内存(不包括引用对象);
Retained Size:这些对象所占用内存(包括应用对象),内存释放时,实际释放的大小就是这里的大小;
下一步就进去我们App的包结构中看看有哪些对象没有被释放,这里是指com那个包
MainActivity是我们的主界面,发现一个MemoryActivity没有被回收,按理来说这个对象是要被回收的,所以首先怀疑这个activity发生了泄露。
现在定位到了MemoryActivity对象发生了泄露,但还不知道它被谁强引用的,为了更好地分析出内存泄露发生的原因,我们就要用到MAT(Memory Analyzer Tool)工具来分析hprof文件了。
2.2 MAT工具分析内存泄露
【没有这个工具的小伙伴可以戳这里–>Eclipse Memory Analyzer Open Source Project】
用Dump Java Heap生成的hprof文件是不能用MAT打开的,所以先转化一下,首先在Android Studio中将hprof文件导出到电脑上,点击下面按钮
然后通过cmd命令:hprof-conv [原文件全路径名] [转换后文件全路径名],做一个转换操作,再使用MAT工具打开
MAT的功能其实蛮多的,这里就不一一介绍了,主要讲解怎么去查找内存泄露的原因,点击左下角的Histogram,显示如下
因为我们已经知道哪个对象发生内存泄露了,直接搜索MemoryActivity找到此对象,右键打开如下
选择“merge shortest Path to GC Roots” -> “exclude all phantom/weak/soft etc. references”,把虚引用,弱引用,软引用全部进行过滤掉,只查看对当前对象的强引用,结果如下:
发现是EventBus的强引用,回去MemoryActivity查看代码, 原来是因为EventBus没有在onDestroy方法中进行注销,所以泄露了。
内存抖动是指大量对象在短时间被创建又在短时间内被回收释放,瞬间产生大量的对象会严重占用虚拟机新生代的内存区域,当达到阈值时就会触发GC,如果GC被频繁触发,势必影响到UI界面的流畅性能。
常见发生内存抖动的地方, 比如在for循环中被频繁创建某个对象,或者其他循环中,这个问题我们平时编程的过程要多加注意,也可以通过android studio内存检测工具查看内存的变化趋势。
Bitmap占用的内存空间非常大,我们使用的过程要非常慎重,一般我们都会通过使用第三方框架来进行图片的管理,从而避免Bitmap使用带来的种种问题,常见的框架有Glide、Picasso等等。