[英]Java OutOfMemoryError strange behaviour
假設我們的最大內存為256M,為什么這段代碼有效:
public static void main(String... args) {
for (int i = 0; i < 2; i++)
{
byte[] a1 = new byte[150000000];
}
byte[] a2 = new byte[150000000];
}
但是這個扔了一個OOME?
public static void main(String... args) {
//for (int i = 0; i < 2; i++)
{
byte[] a1 = new byte[150000000];
}
byte[] a2 = new byte[150000000];
}
為了保持透視,請考慮使用-Xmx64m
運行此代碼:
static long sum;
public static void main(String[] args) {
System.out.println("Warming up...");
for (int i = 0; i < 100_000; i++) test(1);
System.out.println("Main call");
test(5_500_000);
System.out.println("Sum: " + sum);
}
static void test(int size) {
// for (int i = 0; i < 1; i++)
{
long[] a2 = new long[size];
sum += a2.length;
}
long[] a1 = new long[size];
sum += a1.length;
}
根據您是進行預熱還是跳過它,它會吹還是不吹。 這是因為JITted代碼在var中正確地為null
,而解釋的代碼則沒有。 這兩種行為在Java語言規范下都是可以接受的,這意味着你受JVM的支配。
在OS X上使用Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)
進行測試。
用for
循環查看字節碼(簡單代碼,不帶sum
變量):
static void test(int);
Code:
0: iconst_0
1: istore_1
2: goto 12
5: iload_0
6: newarray long
8: astore_2
9: iinc 1, 1
12: iload_1
13: iconst_1
14: if_icmplt 5
17: iload_0
18: newarray long
20: astore_1
21: return
沒有:
static void test(int);
Code:
0: iload_0
1: newarray long
3: astore_1
4: iload_0
5: newarray long
7: astore_1
8: return
沒有明確的null
荷蘭國際集團出在這兩種情況下,但要注意的是,在沒有例如相同的內存位置實際上是重復使用,相比之下同例如 。 如果有的話,這將導致與觀察到的行為相反的期望。
根據我們從字節碼學到的知識,嘗試運行:
public static void main(String[] args) {
{
long[] a1 = new long[5_000_000];
}
long[] a2 = new long[0];
long[] a3 = new long[5_000_000];
}
沒有OOME被拋出 。 注釋掉a2
的聲明,它又回來了。 我們分配更多 ,但占用更少 ? 看一下字節碼:
public static void main(java.lang.String[]);
Code:
0: ldc #16 // int 5000000
2: istore_1
3: ldc #16 // int 5000000
5: newarray long
7: astore_2
8: iconst_0
9: newarray long
11: astore_2
12: ldc #16 // int 5000000
14: newarray long
16: astore_3
17: return
用於a1
的位置2重用於a2
。 對於OP的代碼也是如此,但現在我們用一個無害的零長度數組覆蓋該位置,並使用另一個位置來存儲對我們的巨大數組的引用。
Java語言規范沒有指定必須收集任何垃圾對象,並且JVM規范僅表示在方法完成時將具有局部變量的“框架”作為一個整體銷毀。 因此,我們目睹的所有行為都在書中。 對象的不可見狀態(在由keppil鏈接的文檔中提到)只是描述在某些實現中和在某些情況下發生的事情的一種方式,但絕不是任何類型的規范行為。
這是因為雖然a1
不在括號之后的范圍內,但是在方法返回之前它處於一個名為invisible的狀態。
大多數現代JVM在離開作用域時都不會將變量a1
設置為null
(實際上,內部括號是否存在甚至不會更改生成的字節代碼),因為它非常無效,並且通常不會沒關系 因此,在方法返回之前,不能對a1
進行垃圾回收。
您可以通過添加行來檢查
a1 = null;
在括號內,這使程序運行正常。
隱形術語和解釋來自這篇舊論文: http://192.9.162.55/docs/books/performance/1st_edition/html/JPAppGC.fm.html
: http://192.9.162.55/docs/books/performance/1st_edition/html/JPAppGC.fm.html
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.