簡體   English   中英

Java在真正內存不足之前拋出內存不足異常?

[英]Java throwing out of memory exception before it's really out of memory?

我希望創建一個大的 int 數組,它幾乎可以填滿 JVM 可用的所有內存。 以這段代碼為例:

    final int numBuffers = (int) ((runtime.freeMemory() - 200000L) / (BUFFER_SIZE));
    System.out.println(runtime.freeMemory());
    System.out.println(numBuffers*(BUFFER_SIZE/4)*4);
    buffers = new int[numBuffers*(BUFFER_SIZE / 4)];

當以 10M 的堆大小運行時,這會拋出 OutOfMemoryException,盡管 printlns 的輸出是:

9487176
9273344

我知道數組會有一些開銷,但不是 200k,對吧? 為什么 java 無法為它聲稱有足夠空間的東西分配內存? 在 Java 運行它之前,我必須將減去的常量設置為 4M 左右(此時 printlns 看起來更像是:9487176 5472256)

更令人困惑的是,如果我用二維數組替換緩沖區:

buffers = new int[numBuffers][BUFFER_SIZE / 4];

然后它使用上面顯示的 200k 減法毫無怨言地運行 - 即使兩個數組中存儲的整數數量相同(並且二維數組的開銷不會大於一維數組的開銷,因為它得到了所有那些對要存儲的其他數組的引用)。

有任何想法嗎?

VM 會將堆內存划分為不同的區域(主要用於垃圾收集器),因此當您嘗試分配幾乎整個堆大小的單個對象時,您將耗盡內存。

此外,JRE 已經用完了一些內存。 200k 對於今天的內存大小來說不算什么,10M 堆對於大多數應用程序來說幾乎是不切實際的小。

數組的實際開銷相對較小,在 32 位 VM 上它的 12 字節 IIRC(如果大小小於最小粒度,即 AFAIK 8 字節,則會浪費什么)。 所以在最壞的情況下,每個數組的開銷大約為 19 字節。

請注意,Java 沒有 2D(多維)數組,它在內部將其實現為數組的數組。

在 2D 情況下,您正在分配更多、更小的對象。 內存管理器反對單個大對象占用大部分堆。 為什么這是令人反感的是垃圾收集方案的一個細節——這可能是因為類似它的東西可以在幾代之間移動較小的對象,而堆不會容納移動單個大對象。

這可能是由於內存碎片和 JVM 無法在給定當前堆的情況下分配該大小的數組。

假設您的堆有 10 x長:

xxxxxxxxxx 

然后,你在這里分配一個對象0 這使您的堆看起來像:

xxxxxxx0xx

現在,您不能再分配那 10 個x空間。 你甚至不能分配 8 x s,盡管可用內存是 9 x s。

事實上,數組的數組不會遇到同樣的問題,因為它不連續。

編輯:請注意,以上是對問題的非常簡單的看法。 當堆中需要空間時,Java 的垃圾收集器將嘗試收集盡可能多的內存,如果真的、真的有必要,會嘗試壓縮堆。 但是,某些對象可能不可移動或不可收集,從而產生堆碎片並使您處於上述情況。

您還必須考慮許多其他因素,其中一些包括:VM(不太可能)或您的應用程序(對於簡單場景也不太可能)的內存泄漏,使用Runtime.freeMemory()的不可靠性( GC 可能會在調用后立即運行,並且可用的空閑內存可能會發生變化),每個特定 JVM 的實現細節等。

關鍵是,根據經驗,不要總是期望您的應用程序可以使用全部的Runtime.freeMemory()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM