Android 内存优化-内存抖动

1.内存抖动

内存抖动是Android性能优化中内存优化的一种情况。内存抖动主要是由于频繁的创建和销毁对象导致的。

在程序里,每创建一个对象,就会有一块内存分配给它。每分配一块内存,程序的可用内存也就少一块。当程序被占用的内存达到一定临界程度,垃圾回收器GC(Garbage Collector)就会出动,来释放掉一部分不再被使用的内存。

比如Android里的View.onDraw()方法在每次需要重绘的时候都会被调用,这就意味着,如果在 onDraw()里写了创建对象的代码,在界面频繁刷新的时候,就会频繁创建出一大批只被使用一次的对象,这就会导致内存占用的迅速攀升;然后很快,可能就会触发GC的回收动作,也就是这些被创建出来的对象被GC回收掉。

垃圾内存太多了就被清理掉,这是Java的工作机制,这不是问题。问题在于,频繁创建这些对象会造成内存不断地攀升,在刚回收了之后又迅速涨起来,那么紧接着就是又一次的回收,这么往复下来,最终导致一种循环,一种在短时间内反复地发生内存增长和回收的循环。这种循环往复的状态就叫做 Memory Churn,即内存抖动。

所以,内存抖动实际上就是内存频繁的分配或回收动作执行,导致内存不稳定的一种现象。如果说分配的频率大于回收的频率,那么最终就会导致OOM的发生。

内存抖动不处理的话,最终可能导致手机卡顿,甚至OOM。

2.举例

以字符串拼接为例:

public String addStr(String[ ] values) {

    String result = null;

    for(int i = 0; i < values.length; i++) {

        result += values[i];

    }

    return result;

}

这是字符串的拼接,字符串拼接时会创建StringBuilder对象。因为字符串使用+进行拼接时,每执行一次result += values[i];,都等同于执行一次result = new StringBuilder().append(values[i]).append(result).toString();,也就是说每使用一次+就会使用new StringBuilder()去创建一个StringBuilder对象。这样就造成了在for循环里短时间内创建大量对象,而这些对象在一次for循环完成后就没有价值了,GC就会对它们进行回收,因此就很可能造成内存抖动。

3.内存抖动的危害

①导致程序卡顿

内存抖动会导致程序卡顿。因为内存抖动伴随着频繁的GC,而GC有一个特性,就是STW(stop the world),STW就会使程序出现卡顿。

②导致OOM

内存抖动会是产生内存碎片,而内存碎片过多以后,在新建一个大对象时,就会出现OOM(内存碎片会造成无法提供连续的内存空间)

注意:Android8.0以前,内存抖动会导致OOM,而在Android8.0以后,ART支持标记并整理,就不会导致OOM了。

4.profiler检测内存抖动

大循环中创建对象、自定义View的onDraw()方法中创建对象(屏幕绘制与动画执行时会频繁调用onDraw())是最常见的引起内存抖动的场景。

内存抖动可采用Android Profiler进行检测,可截取某段时间进行对象分析,查看哪些对象被频繁创建。

对上面的例子利用Android Profiler进行检测:

bf44e6fd635c46d685b2d082de340c96.webp

 Record截取一段时间进行分析:

71c6e45171784c5fafb98c1d11a5858b.webp

可以看到创建了大量的StringBuilder对象,也就是在循环中频繁创建对象,GC回收频繁,导致内存抖动。

针对大循环引起的内存抖动,解决办法就是将对象创建放到循环外,对于无法避免的创建对象情况,可采用对象池模型进行缓存,复用对象,需注意用完后要手动释放对象池中对象。

再来看看自定义View的onDraw()方法中创建对象引起内存抖动的情况:

public class IOSStyleLoadingView extends View{

    ……

    @override

    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);

        Paint paint = new Paint();

        paint.setColor(Color.RED);

        ……

        Path path = new Path();

        path.moveTo(100,100);

        ……

    }

}

由于屏幕绘制与动画执行时会频繁调用onDraw()方法,因此在onDraw()方法中创建对象就很容易引起内存抖动。

c469021a039d4c3ca4a2d05051d5f097.jpg

使用profiler很容易就定位出引起内存抖动的原因是在onDraw()方法里创建对象了。

这里解决办法也很简单,就是把Paint和Path的创建移出onDraw()方法就可以了。

猜你喜欢

转载自blog.csdn.net/zenmela2011/article/details/125070508