Bootstrap

内存泄漏和内存溢出以及原因和解决方案

1.什么是内存泄漏?

  Java 中的内存泄漏是指应用程序不再需要的对象在 Java 虚拟机 (JVM) 中仍然存在的状态。通俗来讲就是生命周期长的对象持有生命周期短的对象,导致GC无法回收本该需要回收的对象。
 

2.造成内存泄露的原因有哪些?

(1)非静态内部类造成的内存泄漏

  那么为什么非静态内部类会造成内存泄漏呢?

看个例子

public class Outer {
    private String TAG="Outer";
    private Runnable runnable = new Runnable(){
        public void run(){
            System.out.println("inner run: " + TAG);
        }
    };
}

通过javac Outer.java 我们得到两个class文件

Outer.class

public class Outer {
    private String TAG = "Outer";
    private Runnable runnable = new Runnable() {
        public void run() {
            System.out.println("inner run: " + Outer.this.TAG);
        }
    };

    public Outer() {
    }
}

Outer$1.class

class Outer$1 implements Runnable {
    Outer$1(Outer var1) {
        this.this$0 = var1;
    }

    public void run() {
        System.out.println("inner run: " + this.this$0.TAG);
    }
}

看到没有,在Outer$1.class中,Outer$1是Runnable,我们可以看到在Outer$1类函数中,传了外部类Outer对象,this.this$0是指向Outer对象。Outer$1持有Outer,也就是Runnable持有Outer。

结论:非静态内部类持有外部类的引用

看下静态内部类:

public class Outer {
    private static String TAG="Outer";
    private static Runnable runnable = new Runnable(){
        public void run(){
            System.out.println("inner run: " + TAG);
        }
    };
}

通过javac Outer.java命令

我们看到Outer$1.class

class Outer$1 implements Runnable {
    Outer$1() {
    }
    public void run() {
        System.out.println("inner run: " + Outer.TAG);
    }
}

我们可以得到Outer$1并没有Outer,所以静态内部类不持有外部类。

解决方案:使用静态内部类替换内部类解决非静态内部类造成的内存泄漏。

(2)单例造成的内存泄漏

public class SingleInstance {

    private Context context;
    private static SingleInstance instance;

    private SingleInstance(Context context) {
        this.context = context.getApplicationContext();
    }

    public static SingleInstance getInstance(Context context) {
        if (instance == null) {
            synchronized (SingleInstance.class) {
                if (instance == null) {
                    instance = new SingleInstance(context);
                }
            }
        }
        return instance;
    }

}

如果 SingleInstance.getInstance(context)传入的是Activity的context,那么该Activity就会被单例对象所持有,他的生命周期等于整个应用程序的生命周期,当Activity退出了就不会被回收,造成了内存泄漏。

如果传入的是 Application 的 Context,因为 Application 的生命周期就是整个应用的生命周期,这样是没有问题的。

3)Handler造成的内存泄漏
   1.匿名内部类:

Handler handler=new Handler(){
  @Override
  public void handleMessage(Message msg) {
    super.handleMessage(msg);
  }
};

2.非静态内部类(同上面的非静态内部类造成的内存泄漏)


  protected class AppHandler extends Handler {

    @Override
    public void handleMessage(Message msg) {
      switch (msg.what) {
       
      }
    }
  }

解决方案:静态内部类+弱引用

  private static class MyHandler extends Handler {
    WeakReference<Activity> activity;

    MyHandler(Activity activity){
      this.activity=new WeakReference<Activity>(activity);
    }

    public void handleMessage(Message message){
      switch (message.what){
       
      }
    }
  }

当Activity销毁的时

   // 清空消息队列,移除对外部类的引用
  @Override
  protected void onDestroy() {
    super.onDestroy();
    mHandler.removeCallbacksAndMessages(null);

  }

(4)资源对象使用后未及时关闭

广播BraodcastReceiver文件流Fire图片资源Bitmap数据库游标等在Activity/Fragment的onDestory方法内要关闭掉。

3、内存泄漏的危害

1.一次的内存泄漏可能看不出来什么影响,但是较多的内存泄漏会造成应用卡顿。因为系统分配给每个应用的内存是有限的,内存泄漏会导致其他组件的可用内存变少,一方面系统GC频率变高,GC时所有进程都会等待,会造成系统变卡顿。另一方面内存变少,可能使得系统额外分配给该对象一些内存,而影响整个系统的运行情况

2、导致程序运行崩溃(OOM):一旦内存不足以为某些对象分配所需要的空间,将会导致程序崩溃,造成体验进一步变差。

4.如何来检测内存溢出的情况呢?

见上一篇文章:leakCanary检测内存泄漏的原理(持续更新)_程序猿yz的博客-CSDN博客_leakcanary

本文参考了:为啥非静态内部类能持有外部类? - 腾讯云开发者社区-腾讯云

;