[英]GC takes too much time
我正在使用 IntelliJ 對 Java 堆進行一些測試,但出現此錯誤:
java.lang.OutOfMemoryError: GC overhead limit exceeded.
我知道當 GC 花費太多時間時會發生這種情況,但我無法理解為什么 GC 發生得如此頻繁並花費如此多的時間。
設計:
在主線程中,我重復創建新對象,然后將它們放入映射中。 為了防止收集這些對象,我將在將它們全部放入地圖后打印這些對象。
在監視器線程中,我每次創建新對象時都會打印當前的空閑堆大小。
運行時堆大小配置:
-Xms5m -Xmx5m
測試代碼:
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Example {
public static ReentrantLock lock = new ReentrantLock(true);
public static Condition writeCondition = lock.newCondition();
public static Condition monitorCondition = lock.newCondition();
public static void main(String[] args) throws Exception {
new Thread(new Monitor(lock, writeCondition, monitorCondition)).start(); // start a monitor thread
Map<Integer, Object> map = new HashMap<>();
for (int count = 0; count < 10000000; count++) {
lock.lock();
try {
// every time create a new Object, I will use monitor thread print current free heap size
monitorCondition.signal();
map.put(count, new Object());
writeCondition.await();
} finally {
lock.unlock();
}
}
for (Map.Entry entry : map.entrySet()) {
// keep reference to these Objects, so they will not be garbage collected.
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
}
}
class Monitor implements Runnable {
ReentrantLock lock;
Condition writeCondition;
Condition monitorCondition;
public Monitor(ReentrantLock lock, Condition writeCondition, Condition monitorCondition) {
this.lock = lock;
this.writeCondition = writeCondition;
this.monitorCondition = monitorCondition;
}
@Override
public void run() {
int count = 0;
while (true) {
lock.lock();
try {
writeCondition.signal();
long heapFreeSize = Runtime.getRuntime().freeMemory();
System.out.println(count + " times run monitor : ");
System.out.println("heapFreesize : " + heapFreeSize);
System.out.println();
count++;
monitorCondition.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
結果:
60005 times run monitor :
heapFreesize:603896
Exception in thread "Thread-0" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.nio.CharBuffer.wrap(CharBuffer.java:373)
at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
at java.io.PrintStream.newLine(PrintStream.java:545)
at java.io.PrintStream.println(PrintStream.java:696)
at hello.Monitor.run(Example.java:58)
at java.lang.Thread.run(Thread.java:748)
當可用內存變低時,GC 會嘗試通過收集堆中無法訪問的對象來釋放一些內存。 檢查對象是否不可訪問不是一件容易的事,需要一些時間。
當您的所有(或大部分)對象仍然在某處被引用時,這是浪費時間,因為沒有回收內存。 因此,當 JVM 識別出大量運行時用於運行 GC 而不是做一些有用的事情時,程序會以OutOfMemoryError
終止。
在JVM
參數中,您已指定-Xms5m -Xmx5m
因此,堆大小設置為5MB
即5242880 bytes
。 現在,由於您已經執行了10000000
次循環,因此您在Map
變量map
中分配了10000000
Integer
和Object
類型的Object
。 現在,如果您查看 features,那么您將意識到您平均為map
的單個條目提供了0.524288
字節。 但是如您所知, Integer
對象的大小超過4 bytes
(因為int
大小為4 bytes
而Integer
包裝了int
類型)。
簡而言之,您沒有為JVM
堆分配足夠的內存,這就是為什么您會收到異常java.lang.OutOfMemoryError: GC overhead limit exceeded.
.
注意:在查看您的程序的以下輸出后,我意識到單個條目占用了848 bytes
的內存(請參閱下面的計算)。 所以你至少需要10000000 * 848 = 8480000000 bytes
即8088 MB
內存(物理或虛擬內存):
heapFreesize : 32344
4699761 times run monitor :
heapFreesize : 31496
4699762 times run monitor :
heapFreesize : 30648
4699763 times run monitor :
heapFreesize : 29800
4699764 times run monitor :
heapFreesize : 28952
4699765 times run monitor :
heapFreesize : 28104
4699766 times run monitor :
heapFreesize : 27256
4699767 times run monitor :
heapFreesize : 26408
4699768 times run monitor :
heapFreesize : 25560
...
嘗試為JVM
分配更多的堆,您將不會收到此錯誤。 有關Java HotSpot VM Options
更多信息,請參見此處和此處。
計算:在提供的輸出(由我在上面發布)中,正在打印可用內存。 現在,如果您進行計算,那么您會發現向地圖添加一個條目后內存之間的差異是848
。 因此我可以說, map
的單個條目(不是單個對象,因為有 2 個對象 - Integer 類和 Object 類)消耗848 byres
32344 - 31496 = 848
例如32344 - 31496 = 848
; 31496 - 30648 = 848
; 30648 - 29800 = 848
等等。
另請注意,如果您尚未將-Xms
和-Xmx
開關設置為相同的值(因為我在執行示例運行時未使用這些開關),則JVM
將根據需要增加或減少其堆大小。
注意:您的代碼在邏輯上似乎不正確,因為使用了writeCondition.signal();
和monitorCondition.await();
不sync
。 該計划awaiting infinitely
。 我認為它最終陷入困境。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.