Android性能优化之内存

前言

  • 关于 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() 释放内存。

  • 对象的注册与反注册没有成对出现造成的内存泄露;譬如注册广播接收器、注册观察者(典型的譬如数据库的监听)等。

四、内存泄露检测工具