new arraylist内存_如何避免内部类中的内存泄漏
我先假設讀者已經熟悉在Java代碼中使用嵌套類的基礎知識。在本文里,我將展示嵌套類的陷阱,內部類在JVM中引起內存泄漏和內存不足錯誤的地方。之所以會發生這種類型的內存泄漏,是因為內部類必須始終能夠訪問其外部類。從簡單的嵌套過程到內存不足錯誤(并可能關閉JVM)是一個過程。我們一步步看他是如何產生的。
步驟1:內部類引用其外部類
內部類的任何實例都包含對其外部類的隱式引用。例如,考慮以下帶有嵌套的EnclosedClass非靜態成員類的EnclosingClass聲明:
public class EnclosingClass
{
public class EnclosedClass
{
}
}
為了更好地理解這種連接,我們可以將上面的源代碼(javac EnclosingClass.java)編譯為EnclosingClass.class和EnclosingClass $ EnclosedClass.class,然后檢查后者的類文件。
JDK包含用于反匯編類文件的javap(Java打印)工具。在命令行上,使javap帶有EnclosingClass $ EnclosedClass,如下所示:
javap EnclosingClass$EnclosedClass
我們可以觀察到以下輸出,該輸出揭示了一個隱含的 final的 EnclosingClass this $ 0字段,該字段包含對EnclosingClass的引用:
Compiled from "EnclosingClass.java"
public class EnclosingClass$EnclosedClass {
final EnclosingClass this$0;
public EnclosingClass$EnclosedClass(EnclosingClass);
}
步驟2:構造函數獲取封閉的類引用
上面的輸出顯示了帶有EnclosingClass參數的構造函數。使用-v(詳細)選項執行javap,可以觀察到構造函數在this $ 0字段中保存了EnclosingClass對象引用:
final EnclosingClass this$0;
descriptor: LEnclosingClass;
flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
public EnclosingClass$EnclosedClass(EnclosingClass);
descriptor: (LEnclosingClass;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LEnclosingClass;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."":()V
9: return
LineNumberTable:
line 3: 0
步驟3:聲明一個新方法
接下來,我們另一個類中聲明一個方法,實例化EnclosingClass,然后實例化EnclosedClass。例如:
EnclosingClass ec = newEnclosingClass();
ec.newEnclosedClass();
下面的javap輸出顯示了此源代碼的字節碼轉換。第18行顯示對EnclosingClass $ EnclosedClass(EnclosingClass)的調用。
0: new #2 // class EnclosingClass
3: dup
4: invokespecial #3 // Method EnclosingClass."":()V
7: astore_1
8: new #4 // class EnclosingClass$EnclosedClass
11: dup
12: aload_1
13: dup
14: invokestatic #5 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
17: pop
18: invokespecial #6 // Method EnclosingClass$EnclosedClass."":(LEnclosingClass;)V
21: pop
22: return
內存泄漏的解剖
在以上示例中,根據應用程序代碼,可能會耗盡內存并收到內存不足錯誤,從而導致JVM終止。下面的清單演示了這種情況。
import java.util.ArrayList;
class EnclosingClass
{
private int[] data;
public EnclosingClass(int size)
{
data = new int[size];
}
class EnclosedClass
{
}
EnclosedClass getEnclosedClassObject()
{
return new EnclosedClass();
}
}
public class MemoryLeak
{
public static void main(String[] args)
{
ArrayList al = new ArrayList<>();
int counter = 0;
while (true)
{
al.add(new EnclosingClass(100000).getEnclosedClassObject());
System.out.println(counter++);
}
}
}
EnclosingClass聲明一個引用整數數組的私有數據字段。數組的大小傳遞給此類的構造函數,并實例化該數組。
EnclosingClass還聲明EnclosedClass,一個嵌套的非靜態成員類,以及一個實例化EnclosedClass的方法,并返回此實例。
MemoryLeak的main()方法首先創建一個java.util.ArrayList來存儲EnclosingClass.EnclosedClass對象。現在,觀察內存泄漏是如何發生的。
將計數器初始化為0后,main()進入無限while循環,該循環重復實例化EnclosedClass并將其添加到數組列表中。然后打印(或遞增)計數器。
每個存儲的EnclosedClass對象都維護對其外部對象的引用,該對象引用100,000個32位整數(或400,000字節)的數組。在對內部對象進行垃圾收集之前,無法對外部對象進行垃圾收集。最終,該應用程序將耗盡內存。
javac MemoryLeak.java
java MemoryLeak
我們將觀察到如下輸出(當然在不同的機器上,最后的數字可能不一樣):
7639
7640
7641
7642
7643
7644
7645
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at EnclosingClass.(MemoryLeak.java:9)
at MemoryLeak.main(MemoryLeak.java:30)
往期精選
CHOICENESS
是兄弟,就來“kan”我總結
以上是生活随笔為你收集整理的new arraylist内存_如何避免内部类中的内存泄漏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android pda出入库管理,出入库
- 下一篇: python模块导入_Python模块及