簡體   English   中英

GC 花費太多時間

[英]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因此,堆大小設置為5MB5242880 bytes 現在,由於您已經執行了10000000次循環,因此您在Map變量map中分配了10000000 IntegerObject類型的Object 現在,如果您查看 features,那么您將意識到您平均為map的單個條目提供了0.524288字節。 但是如您所知, Integer對象的大小超過4 bytes (因為int大小為4 bytesInteger包裝了int類型)。

簡而言之,您沒有為JVM堆分配足夠的內存,這就是為什么您會收到異常java.lang.OutOfMemoryError: GC overhead limit exceeded. .

注意:在查看您的程序的以下輸出后,我意識到單個條目占用了848 bytes的內存(請參閱下面的計算)。 所以你至少需要10000000 * 848 = 8480000000 bytes8088 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.

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