Android内存优化

276 阅读4分钟

(一)内存抖动:

短时间内有大量对象创建销毁,它伴随着频繁的GC;

比较典型的就是字符串拼接造成的内存抖动。 例如:我们通常会这么做字符串拼接操作

 private String  addStr(String[] values) {
   
   String result = "";
   for (int i = 0; i < values.length;i++){

       result +=  i;

   }

   return result;
}

这里我们使用一个插件ASM bytecode Viewer 反编译即可看到对应的字节码指令,可以看到这段代码进行字符串拼接操作时会不断的创建StringBuilder对象 也就是说当values的长度为1000时会创建1000次的StringBuilder对象,而这些对象对于我们来说,都是零时产生的对象,这种情况就极可能造成内存抖动。此处我们可以自己创建一个StringBuilder对象,代码如下:

  public String addStr(String[] values) {
   
    StringBuilder result = new StringBuilder();
    // 10000
    for (int i = 0; i < values.length; i++) {
        result.append(result).append(values[i]);
    }
    return result.toString();
}

这样我们就只创建了一次StringBuilder,极大的减少了零时对象的创建,避免内存抖动问题。

注:Android里面使用的CMS垃圾回收器, 内存抖动会造成的结果,

1.卡顿:

当我们的App不断产生新的对象时,App所消耗的内存就越来越多,当内存占用到一定的程度的时候,系统就会使用GC进行垃圾回收,而当进行GC回收的时候,应用的主线程,或者其他线程就会被挂起,从而导致用户的操作得不到及时的反馈,引起卡顿。

2.OOM:

CMS垃圾回收器老年代采用标记清除算法,这种在回收之后会产生大量的内存碎片,内存不连续,此时去申请内存时就可能产生OOM。

注:CMS 新生代采用的复制算法,不会产生大量的内存碎片。

预防抖动:

*避免在循环中创建对象

*避免在频繁调用的方法中创建对象

*允许复用的情况下,使用对象池进行缓存,如: Handler的Message单链表(obtain);

(二)内存泄漏

程序中已动态分配的堆内存由于某种原因未释放或无法释放,造成系统内存的浪费。长生命周期对象的强引用,从而导致短生命周期对象无法被回收。

可达性分析法 通过一系列称为"GC ROOTS"的对象作为起始点,从这些节点向下搜索,搜索所有的引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。

这里我们需要知道java中的四种引用方式:

强引用:是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它,

软引用(SoftReference):定义一些还有用但非必须的对象。对于软引用关联的对象,GC不会直接回收,而是在系统将要内存溢出之前才会触发GC将这些对象进行回收。

弱引用:同样定义非必须对象,被弱引用关联的对象在 GC执行时会被直接回收。

虚引用(PhantomReference): 与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

通常我们在使用Handler时,会使用如下方式:

   mHandler = new Handler();
   mHandler.postDelayed(new Runnable(){
       
       public void run(){
           
       }
   },1000);
  
此处容易发生内存泄漏,因为 new Runnable()是匿名内部类持有activity的引用。

优化:

方式一: 在onDestroy()方法中调用: mhandler.removeCallbackAndMessage(null);

方式二: 将匿名内部类改为静态内部类:

  static class R1 implements Runnable {
    WeakReference<SecondActivity> activity;

    public R1(SecondActivity activity) {
        this.activity = new WeakReference<SecondActivity>(activity);
    }

    @Override
    public void run() {
        System.out.println(activity.get().i);
    }
}

Handler mHandler;

使用:mHandler.postDelayed(new R1(this), 10000

这里采用的是弱引用,当GC回收时,会将activity回收掉。

内存问题常见的场景:

*集合类: 当使用集合时,只有添加元素,没有对应的删除元素 如EventBus 只有注册没有注销!

*静态成员/单例 作为GC ROOT,持有短生命周期引用(如Activity)导致 其短生命周期对象无法释放。

*未关闭/释放资源 如FileOutputStream 未Close。

*非静态内部类 如Handler postDelayed一个匿名Runnable,退出actiivty时消息没处理完。

*系统问题 WebView 、InputMethodManager等;