new arraylist内存_如何避免内部类中的内存泄漏
我先假設(shè)讀者已經(jīng)熟悉在Java代碼中使用嵌套類(lèi)的基礎(chǔ)知識(shí)。在本文里,我將展示嵌套類(lèi)的陷阱,內(nèi)部類(lèi)在JVM中引起內(nèi)存泄漏和內(nèi)存不足錯(cuò)誤的地方。之所以會(huì)發(fā)生這種類(lèi)型的內(nèi)存泄漏,是因?yàn)閮?nèi)部類(lèi)必須始終能夠訪(fǎng)問(wèn)其外部類(lèi)。從簡(jiǎn)單的嵌套過(guò)程到內(nèi)存不足錯(cuò)誤(并可能關(guān)閉JVM)是一個(gè)過(guò)程。我們一步步看他是如何產(chǎn)生的。
步驟1:內(nèi)部類(lèi)引用其外部類(lèi)
內(nèi)部類(lèi)的任何實(shí)例都包含對(duì)其外部類(lèi)的隱式引用。例如,考慮以下帶有嵌套的EnclosedClass非靜態(tài)成員類(lèi)的EnclosingClass聲明:
public class EnclosingClass
{
public class EnclosedClass
{
}
}
為了更好地理解這種連接,我們可以將上面的源代碼(javac EnclosingClass.java)編譯為EnclosingClass.class和EnclosingClass $ EnclosedClass.class,然后檢查后者的類(lèi)文件。
JDK包含用于反匯編類(lèi)文件的javap(Java打印)工具。在命令行上,使javap帶有EnclosingClass $ EnclosedClass,如下所示:
javap EnclosingClass$EnclosedClass
我們可以觀(guān)察到以下輸出,該輸出揭示了一個(gè)隱含的 final的 EnclosingClass this $ 0字段,該字段包含對(duì)EnclosingClass的引用:
Compiled from "EnclosingClass.java"
public class EnclosingClass$EnclosedClass {
final EnclosingClass this$0;
public EnclosingClass$EnclosedClass(EnclosingClass);
}
步驟2:構(gòu)造函數(shù)獲取封閉的類(lèi)引用
上面的輸出顯示了帶有EnclosingClass參數(shù)的構(gòu)造函數(shù)。使用-v(詳細(xì))選項(xiàng)執(zhí)行javap,可以觀(guān)察到構(gòu)造函數(shù)在this $ 0字段中保存了EnclosingClass對(duì)象引用:
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:聲明一個(gè)新方法
接下來(lái),我們另一個(gè)類(lèi)中聲明一個(gè)方法,實(shí)例化EnclosingClass,然后實(shí)例化EnclosedClass。例如:
EnclosingClass ec = newEnclosingClass();
ec.newEnclosedClass();
下面的javap輸出顯示了此源代碼的字節(jié)碼轉(zhuǎn)換。第18行顯示對(duì)EnclosingClass $ EnclosedClass(EnclosingClass)的調(diào)用。
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
內(nèi)存泄漏的解剖
在以上示例中,根據(jù)應(yīng)用程序代碼,可能會(huì)耗盡內(nèi)存并收到內(nèi)存不足錯(cuò)誤,從而導(dǎo)致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聲明一個(gè)引用整數(shù)數(shù)組的私有數(shù)據(jù)字段。數(shù)組的大小傳遞給此類(lèi)的構(gòu)造函數(shù),并實(shí)例化該數(shù)組。
EnclosingClass還聲明EnclosedClass,一個(gè)嵌套的非靜態(tài)成員類(lèi),以及一個(gè)實(shí)例化EnclosedClass的方法,并返回此實(shí)例。
MemoryLeak的main()方法首先創(chuàng)建一個(gè)java.util.ArrayList來(lái)存儲(chǔ)EnclosingClass.EnclosedClass對(duì)象?,F(xiàn)在,觀(guān)察內(nèi)存泄漏是如何發(fā)生的。
將計(jì)數(shù)器初始化為0后,main()進(jìn)入無(wú)限while循環(huán),該循環(huán)重復(fù)實(shí)例化EnclosedClass并將其添加到數(shù)組列表中。然后打印(或遞增)計(jì)數(shù)器。
每個(gè)存儲(chǔ)的EnclosedClass對(duì)象都維護(hù)對(duì)其外部對(duì)象的引用,該對(duì)象引用100,000個(gè)32位整數(shù)(或400,000字節(jié))的數(shù)組。在對(duì)內(nèi)部對(duì)象進(jìn)行垃圾收集之前,無(wú)法對(duì)外部對(duì)象進(jìn)行垃圾收集。最終,該應(yīng)用程序?qū)⒑谋M內(nèi)存。
javac MemoryLeak.java
java MemoryLeak
我們將觀(guān)察到如下輸出(當(dāng)然在不同的機(jī)器上,最后的數(shù)字可能不一樣):
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
是兄弟,就來(lái)“kan”我總結(jié)
以上是生活随笔為你收集整理的new arraylist内存_如何避免内部类中的内存泄漏的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Android pda出入库管理,出入库
- 下一篇: 汉字的ascii码值范围_ASCII代码