Java內部類持有外部類導致內存泄露的原因與解決方案詳解
簡介
說明
本文介紹Java內部類持有外部類導致內存泄露的原因以及其解決方案。
為什么內部類持有外部類會導致內存泄露?
非靜態(tài)內部類會持有外部類,如果有地方引用了這個非靜態(tài)內部類,會導致外部類也被引用,垃圾回收時無法回收這個外部類(即使外部類已經沒有其他地方在使用了)。
解決方案
1.不要讓其他的地方持有這個非靜態(tài)內部類的引用,直接在這個非靜態(tài)內部類執(zhí)行業(yè)務。
2.將非靜態(tài)內部類改為靜態(tài)內部類。
內部類改為靜態(tài)的之后,它所引用的對象或屬性也必須是靜態(tài)的,所以靜態(tài)內部類無法獲得外部對象的引用,只能從 JVM 的 Method Area(方法區(qū))獲取到static類型的引用。
相關網址
匿名內部類的內存泄露:Java的匿名內部類導致內存泄露--原因/解決方案
為什么要持有外部類
Java 語言中,非靜態(tài)內部類的主要作用有兩個:
當內部類只在外部類中使用時,匿名內部類可以讓外部不知道它的存在,從而減少了代碼的維護工作。
當內部類持有外部類時,它就可以直接使用外部類中的變量了,這樣可以很方便的完成調用,如下代碼所示:
package org.example.a;
class Outer{
private String outerName = "Tony";
class Inner{
private String name;
public Inner() {
this.name = outerName;
}
}
Inner createInner() {
return new Inner();
}
}
public class Demo {
public static void main(String[] args) {
Outer.Inner inner = new Outer().createInner();
System.out.println(inner);
}
}但是,靜態(tài)內部類就無法持有外部類和其非靜態(tài)字段了。比如下邊這樣就會報錯
package org.example.a;
class Outer{
private String outerName = "Tony";
static class Inner{
private String name;
public Inner() {
this.name = outerName;
}
}
Inner createInner() {
return new Inner();
}
}
public class Demo {
public static void main(String[] args) {
Outer.Inner inner = new Outer().createInner();
System.out.println(inner);
}
}報錯:

實例:持有外部類
代碼
package org.example.a;
class Outer{
class Inner {
}
Inner createInner() {
return new Inner();
}
}
public class Demo {
public static void main(String[] args) {
Outer.Inner inner = new Outer().createInner();
System.out.println(inner);
}
}斷點調試
可以看到:內部類持有外部類的對象的引用,是以“this$0”這個字段來保存的。

實例:不持有外部類
代碼
package org.example.a;
class Outer{
static class Inner {
}
Inner createInner() {
return new Inner();
}
}
public class Demo {
public static void main(String[] args) {
Outer.Inner inner = new Outer().createInner();
System.out.println(inner);
}
}斷點調試
可以發(fā)現:內部類不再持有外部類了。

實例:內存泄露
簡介
若內部類持有外部類的引用,對內部類的使用很多時,會導致外部類數目很多。此時,就算是外部類的數據沒有被用到,外部類的數據所占空間也不會被釋放。
本處在外部類存放大量的數據來模擬。
代碼
package org.example.a;
import java.util.ArrayList;
import java.util.List;
class Outer{
private int[] data;
public Outer(int size) {
this.data = new int[size];
}
class Innner{
}
Innner createInner() {
return new Innner();
}
}
public class Demo {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
int counter = 0;
while (true) {
list.add(new Outer(100000).createInner());
System.out.println(counter++);
}
}
}測試
可以看到:運行了八千多次的時候就內存溢出了。

不會內存泄露的方案
簡介
內部類改為靜態(tài)的之后,它所引用的對象或屬性也必須是靜態(tài)的,所以靜態(tài)內部類無法獲得外部對象的引用,只能從 JVM 的 Method Area(方法區(qū))獲取到static類型的引用。
代碼
package org.example.a;
import java.util.ArrayList;
import java.util.List;
class Outer{
private int[] data;
public Outer(int size) {
this.data = new int[size];
}
static class Inner {
}
Inner createInner() {
return new Inner();
}
}
public class Demo {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
int counter = 0;
while (true) {
list.add(new Outer(100000).createInner());
System.out.println(counter++);
}
}
}測試
可以發(fā)現:循環(huán)了四十多萬次都沒有內存溢出。

以上就是Java內部類持有外部類導致內存泄露的原因與解決方案詳解的詳細內容,更多關于Java內存泄露的資料請關注腳本之家其它相關文章!
相關文章
Spring Boot 與 Kotlin 使用Redis數據庫的配置方法
Redis是目前業(yè)界使用最廣泛的內存數據存儲。下面通過本文給大家介紹Spring Boot 與 Kotlin 使用Redis數據庫的配置方法,感興趣的朋友一起看看吧2018-01-01
Eclipse 出現A configuration with this name already exists問題解決方
這篇文章主要介紹了Eclipse 出現A configuration with this name already exists問題解決方法的相關資料,需要的朋友可以參考下2016-11-11

