前两天刷文章偶然翻到一篇因使用非静态内部类时导致内存泄漏的问题,出于好奇自己也动手一试
什么叫内存泄漏
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
通俗点讲就是内存泄漏并不是指的物理上的内存遭到了泄漏,而是指代码中因为某些程序执行完之后没有释放所占用的空间所导致的,比如Java中我们new了一个对象,用完之后,因为某种原因这个对象没有被垃圾回收器回收,这就导致这个对象一直占用着这片空间久而久之导致服务崩溃.
非静态内部类导致内存泄漏的原因
之所以会发生这种类型的内存泄漏,是因为内部类的任何实例都包含对其外部类的隐式引用,且内部类始终能够访问其外部类-并非总是与JVM的计划一起使用。
测试代码
public class OutClass {
private int[] i;
public OutClass(int size) {
this.i = new int[size];
}
public InClass test() {
return new InClass();
}
public class InClass{
}
}
public class ClassTest {
public static void main(String[] args) {
List<OutClass.InClass> list = new ArrayList<>();
int i = 1;
while (true) {
OutClass outClass = new OutClass(10000);
list.add(outClass.test());
System.out.println(i);
i++;
}
}
}
编译之后
我们可以看到多了一个OutClass$InClass.class的文件,拖进IDEA里反编译后有一个this$0的实例
打断点启动程序
this$0的内存地址就是outClass的地址,也就是内部类持有外部类的实例,也就是上图OutClass$InClass对象
放开端点继续执行
执行了84551次,程序就报出了内存溢出的异常
解决此问题也容易,就是给内部类加上static关键字,使其变为静态内部类
反编译后没有了this$0实例
打断点继续测试,此时内部类已经不在持有外部类的实例
异常没有出现