Tuesday, February 23, 2021

內存優化

 1. 常见内存泄漏

造成内存泄漏的根本原因

生命周期较短的某个对象被生命周期更长的对象所持有,导致该对象不能及时释放。

1.1 静态变量导致的内存泄漏

因为静态变量生命周期等于应用程序的生命周期,所以静态变量引用的变量不会被回收掉。这里涉及到 GC Roots 的概念。

下面是两种明显的内存泄漏:

======================================================================

public class MyCouponActivity extends BaseActivity  {
    private static Context sContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_coupon);
        sContext = this;
    }
}
// or
public class MyCouponActivity extends BaseActivity  {
    private static View sView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_coupon);
        sView = new View(this);
    }
}

======================================================================

1.2 非静态内部类(匿名类)内存泄露

注意一下静态匿名内部类和非静态匿名内部类的区别

非静态匿名内部类会持有外部class的强引用。

Handler 需要使用 static 修饰,且持有 Activity 时需要持有 WeakReference的缘故


1.2.1 HANDLER 内存泄漏

使用非静态内部类来实现Handler,lint就会给出警告。这涉及到Handler的原理。

如果Handler中有延迟的任务或者是等待执行的任务队列过长,都有可能因为Handler继续执行而导致Activity发生泄漏

首先,非静态的Handler类会默认持有外部类的引用,包含Activity等

然后,还未处理完的消息(Message)中会持有Handler的引用

还未处理完的消息会处于消息队列中,即消息队列MessageQueue会持有Message的引用

消息队列MessageQueue位于Looper中,Looper的生命周期跟应用一致 

因此,此时的引用关系链是Looper -> MessageQueue -> Message -> Handler -> Activity。所以,这时退出Activity的话,由于存在上述的引用关系,垃圾回收器将无法回收Activity,从而造成内存泄漏。

1.2.2 多线程引起的内存泄露

我们一般使用匿名类等来启动一个线程,如下:

======================================================================

new Thread(new Runnable() {
    @Override
    public void run() {
    }
}).start();

======================================================================

同样,匿名 Thread 类里持有了外部类的引用。当 Activity 退出时,Thread有可能还在后台执行,这时就会发生了内存泄露。

解决方案和上面 Handler 类似:要不就是变成

静态内部类,引用外面资源时使用WeakReference

要不就是在Activity退出时,结束线程。

1.3 其他情况造成的内存泄漏

集合类内存泄露

集合类添加元素后,将会持有元素对象的引用,导致该元素对象不能被垃圾回收,从而发生内存泄漏

 属性动画导致的内存泄漏

属性动画中有一类无限循环的动画,如果Activity中播放此类动画且没有在onDestory方法中去停止动画,那么动画会一直播放下去。我们需要在Activity#onDestory中调用animator.cancel()方法来停止动画。

 网络、文件等流忘记关闭

手动注册广播时,退出时忘记unregisterReceiver()

Service执行完后忘记stopSelf()

EventBus等观察者模式的框架忘记手动解除注册

2 内存管理


No comments:

Post a Comment

n8n index

 【n8n免費本地端部署】Windows版|程式安裝x指令大補帖  【一鍵安裝 n8n】圖文教學,獲得無限額度自動化工具&限時免費升級企業版功能