Java 中的内存泄漏是指应用程序不再需要的对象在 Java 虚拟机 (JVM) 中仍然存在的状态。通俗来讲就是生命周期长的对象持有生命周期短的对象,导致GC无法回收本该需要回收的对象。
(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;
MyHandler(Activity activity){
this.activity=new WeakReference(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方法内要关闭掉。
1.一次的内存泄漏可能看不出来什么影响,但是较多的内存泄漏会造成应用卡顿。因为系统分配给每个应用的内存是有限的,内存泄漏会导致其他组件的可用内存变少,一方面系统GC频率变高,GC时所有进程都会等待,会造成系统变卡顿。另一方面内存变少,可能使得系统额外分配给该对象一些内存,而影响整个系统的运行情况。
2、导致程序运行崩溃(OOM):一旦内存不足以为某些对象分配所需要的空间,将会导致程序崩溃,造成体验进一步变差。
见上一篇文章:leakCanary检测内存泄漏的原理(持续更新)_程序猿yz的博客-CSDN博客_leakcanary
本文参考了:为啥非静态内部类能持有外部类? - 腾讯云开发者社区-腾讯云