陷阱 - 呼叫 System.gc() 是低效的

呼叫 System.gc()(幾乎總是)是一個壞主意。

gc() 方法的 javadoc 指定以下內容:

“呼叫 gc 方法表明 Java 虛擬機器花費了很多精力來回收未使用的物件,以使它們當前佔用的記憶體可用於快速重用。當控制從方法呼叫返回時,Java 虛擬機器已盡最大努力回收所有廢棄物體的空間。“

從中可以得出以下幾點:

  1. 使用建議一詞而不是(說)告訴意味著 JVM 可以自由地忽略該建議。預設的 JVM 行為(最新版本)是遵循建議的,但是這可以通過在啟動 JVM 時設定 -XX:+DisableExplicitGC 來覆蓋。

  2. 短語從所有丟棄的物件中回收空間的最佳努力意味著呼叫 gc 將觸發完整垃圾收集。

那麼為什麼稱 System.gc() 是一個壞主意呢?

首先,執行完整的垃圾收集是很昂貴的。完整的 GC 涉及訪問和標記仍可到達的每個物件; 即每個不是垃圾的物體。如果在沒有太多垃圾需要收集的情況下觸發此操作,那麼 GC 會做很多工作而收益相對較小。

其次,完整的垃圾收集可能會干擾未收集的物件的位置屬性。大致在同一時間由同一執行緒分配的物件往往在記憶體中靠近分配。這很好。同時分配的物件可能是相關的; 即彼此參考。如果你的應用程式使用這些引用,那麼由於各種記憶體和頁面快取效應,記憶體訪問的可能性會更快。不幸的是,完整的垃圾收集往往會移動物件,以便曾經關閉的物件現在更加分開。

第三,執行完整的垃圾收集可能會使你的應用程式暫停,直到收集完成。發生這種情況時,你的應用程式將無響應。

實際上,最好的策略是讓 JVM 決定何時執行 GC,以及執行什麼樣的集合。如果不干擾,JVM 將選擇優化吞吐量或最小化 GC 暫停時間的時間和集合型別。

一開始我們說“……(幾乎總是)一個壞主意……”。事實上,有幾種情況可能是個好主意:

  1. 如果你正在為某些對垃圾收集敏感的程式碼實現單元測試(例如涉及終結器或弱/軟/幻像引用的內容),則可能需要呼叫 System.gc()

  2. 在一些互動式應用程式中,可能存在特定時間點,使用者不關心是否存在垃圾收集暫停。一個例子是遊戲中存在自然暫停的遊戲; 例如,當載入新級別時。