前言
- 关于 Android 中内存的处理也算是老生常谈了,虽然 Java 有自己的 GC 机制,但是如果我们自己没有良好的编写代码习惯,在项目逐渐变大的时候,就容易出现内存上的问题。
正文
一、关于内存的一些概念
静态存储区:程序编译时就分配好,在程序整个运行期间都存在,主要存放静态数据和常量。
栈内存:存放基本类型的变量数据,局部变量,和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中。)
堆内存:用来存放动态产生的数据,比如new产生的对象,数组。
方法区:用来存放已被加载的类的信息、常量、静态变量、编译器编译后的代码。
二、四种引用类型
强引用:JVM 宁可抛出 OOM ,也不会让 GC 回收具有强引用的对象,也是我们用的最多的引用类型
软引用:只有在内存空间不足时,才会被回的对象;
弱引用:在 GC 时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存;
虚引用:任何时候都可以被GC回收,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否存在该对象的虚引用,来了解这个对象是否将要被回收。可以用来作为GC回收Object的标志。
怎么用:
- 个人认为,如果只是想避免 OutOfMemory 异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。
- 还有就是可以根据对象是否经常使用来判断。如果该对象可能会经常使用的,就尽量用软引用。如果该对象不被使用的可能性更大些,就可以用弱引用。
三、关于内存泄露需要注意的地方
内存泄露偶尔有一个倒是无伤大雅,关键是多了就会造成内存溢出,进而导致程序崩溃
创建单例模式的时候 context 对象的引用,使用 application 的 contex
handler:其生命周期与activity不一致,容易导致内存无法正确释放,解决:创建一个 静态Handler内部类,然后对 Handler 持有的对象使用弱引用,这样在回收时也可以回收 Handler 持有的对象,但是这样做虽然避免了 Activity 泄漏,不过 Looper 线程的消息队列中还是可能会有待处理的消息,所以我们在 Activity 的 Destroy 时或者 Stop 时应该移除消息队列 MessageQueue 中的消息 handler.removeMessage(int what);
非静态内部类和匿名内部类:非静态内部类和匿名内部类会持有外部类的引用,导致外部类的资源无法被正常释放,正确做法是:将此类抽取出来为单例模式,按照单例模式的设计思路处理
集合对象及时清除,在 activity 的 onDestory() 方法中,将集合里面的东西 clear ,然后置为 null。
bitmap 对象不再使用的时候,调用 recycle() 释放内存。
对象的注册与反注册没有成对出现造成的内存泄露;譬如注册广播接收器、注册观察者(典型的譬如数据库的监听)等。
四、内存泄露检测工具
实际上就算我们再怎么仔细,还是会出现内存泄露的可能,所以要借助工具来帮我们检测
MAT (memory analyzer tool),下载地址: http://www.eclipse.org/mat/downloads.php
LeakCanary,这是一个开源项目,官方文档: https://github.com/square/leakcanary