Android内存管理之内存抖动、内存泄露、内存溢出

内存抖动

内存抖动的现象就是UI界面的卡顿,出现的原因就是年轻代的频繁GC。JVM的每次年轻代GC都会触发“全世界的暂停(stop-the-world)”,也就会导致卡顿。避免出现内存抖动的几点建议:
1、不要在循环体内创建对象,将创建对象放到循环体外,在循环体内重用一个对象。
2、自定义View的onDraw()onMeasure()方法会多次调用,所以尽量不要在里面创建对象。
3、当需要使用大量的Bitmap的时候,应该将他们缓存到数组中使用。
4、同理,对于有复用的对象,可以用对象池将它缓存起来。

内存溢出

内存溢出意思就是为对象申请不到足够的内存空间了,导致OOM。内存溢出导致的原因可能是内存泄露,也有可能是JVM分配的内存真的不够使用了。在Android手机上,系统为程序最初分配的内存都是一致的,所以开发者尽量不要占用太多的内存空间,没有使用的对象及时置为null。一般情况这种场景是不会出现的,程序员一般只需要关注内存抖动和内存泄露。

内存泄露

在Android手机上,内存是很珍贵的,系统为每个应用分配了固定的内存大小,如果申请不到足够的内存空间,就直接导致内存溢出。以前并没有内存泄露的概念,单纯的以为JVM会管理好所有的内存空间,并不需要开发者操心,但事实上不是。内存泄露的根本原因是一个长生命周期的对象持有了一个短生命周期的对象的引用。下面我们来看下Android上面的有哪些内存泄露的风险。

  • 单例模式导致内存泄露,静态对象的生命周期和应用程序一样长,如果持有一个短生命周期如Activity的context就会导致activity无法被回收,所以应该使用getApplicationContext()进行初始化。
  • 内部Handler如果持有了Activity的强引用,而handler执行没有完成,activity就已经调用onDestory了,就会导致activity无法被回收。
  • 非静态内部类默认持有外部类对象引用。所以导致外部类销毁时仍然持有外部类引用,导致外部类无法被回收。
  • 未去注册,如广播,服务,注册或者启动之后必须要去注册
  • 资源对象未及时释放,如Cursor、Stream、Socket,Bitmap等在使用后要及时关闭。
  • 集合中的对象要及时clear。

Android Studio会对代码中可能出现内存泄露的地方会给出提示,能够避免很多常见的内存泄露。
对于第二种情况的内存泄露,我们一般是使用静态内部类+弱引用的方式去解决,handler不持有activity引用,而是通过弱引用去获得一个弱引用对象。
第三种情况有很多例子场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//线程匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();

new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}.execute();
//线程写成静态内部类
private static class MyThread implements Runnable {
public void run() {
SystemClock.sleep(20000);
}
}

总结

Android关于内存的最佳实践

  • Enum非常占用内存,因为枚举类型的每个属性值都是Object,每声明一个值都会创建内存以便能引用到这个对象,Android中使用@IntDef、@StringDef来替代枚举类型的使用
    1
    2
    3
    Java 的 Enum 的实质是特殊单例的静态成员变量
    Enum 可以在编写器,编译器做到各种静态检查防呆
    Enum 在运行期,所有枚举类作为单例,全部加载到内存中

内存泄露是非常容易出现的,上面只是一些大多数场景,还有很多需要注意的地方,特别是一些框架、组件这样的内存泄露很容易忽视。查找内存泄露的方式也很多,如LeakCanary,还有Android Studio3.0自带的
Android Profiler,熟练使用这些工具能够快速的帮助自己定位内存泄露,提高程序的性能。