微信公眾號:Java大家族
JVM將初始和最大內存大小設置為相同值的好處
啟動應用程序時,我們指定初始內存大小和最大內存大小。對於在 JVM(Java 虛擬機)上運行的應用程序,初始和最大內存大小通過 “-Xms” 和 “-Xmx” 參數指定。如果 Java 應用程序在容器上運行,則通過“-XX:InitialRAMPercentage”和“-XX:MaxRAMPercentage”參數指定它。大多數企業將初始內存大小設置為低於最大內存大小的值。與這種普遍接受的做法相反,將初始內存大小設置為與最大內存大小相同具有如下優勢。讓我們在這篇文章中討論它們。
1. 可用性
假設您正在啟動應用程序,初始堆大小為 2GB,最大堆大小為 24GB。這意味着當應用程序啟動時,操作系統將為您的應用程序分配 2GB 的內存。從那時起,當應用程序開始處理新請求時,將分配額外的內存,直到達到最大 24GB。
假設當您的應用程序的內存消耗正在從2GB增長到24GB的過程中,此時,服務器啟動了其他一些進程,並且這些進程開始消耗內存。這種情況在生產/雲環境中非常常見,尤其是在應用程序與其他進程(如自定義腳本、cron 作業、監視代理等)一起運行時。
發生這種情況時,您的應用程序將遇到以下情況:
“java.lang.OutOfMemoryError:Java heap space”
操作系統將終止您的應用程序,並顯示“內存不足:殺死進程。
這意味着您的應用程序將在事務過程中崩潰。如果應用程序在啟動期間以最大內存啟動,則應用程序將是安全的。操作系統將僅終止內存消耗正在增長的新啟動的腳本/cron 作業,而不會終止在啟動期間內存已完全分配的應用程序。
2. 性能
我們還觀察到,以相同的初始堆大小和最大堆大小啟動的應用程序往往比以較低的初始堆大小啟動的應用程序的性能相對較好。
這是一個真實的案例研究:我們使用記憶密集型應用程序進行測試。此應用程序處理非常大的二進制堆轉儲文件並生成分析報告。在這個應用程序中,我們反復分析一個11GB大小的二進制文件,這樣它就會給操作系統帶來內存壓力。
我們進行了兩個測試場景:
方案 1:我們將初始堆大小設置為 2GB,最大堆大小設置為 24GB。
方案 2:我們將初始堆大小和最大堆大小都設置為 24GB。
在場景 1 中,我們觀察到平均響應時間為 385.32 秒,而在場景 2 中,我們觀察到平均響應時間為 366.55 秒。響應時間縮短了 5.11%。響應時間的這種改善是由於以下兩個原因:
操作系統的內存分配和解除分配
GC 暫停時間影響
讓我們在這里討論它們:
從操作系統分配和解除分配內存
當您為初始堆大小和最大堆大小設置了不同的大小時,JVM 將不得不與操作系統協商,以便在需要時分配內存。同樣,當應用程序對內存的需求在運行時出現故障時,操作系統將占用分配的內存。這種持續的分配和解除分配將增加應用程序的開銷。
場景 1:內存分配波動(按 GCeasy 繪制的圖表)
上圖顯示了場景 1 JVM 的已分配和已解除分配的內存。從圖表中,您可以注意到內存在不斷波動(在 2GB 到 24GB 之間波動)。當應用程序處理堆轉儲時,內存最多可達 24GB。處理后,內存將回落到 2GB。當它再次處理新的堆轉儲時,內存會回彈到 24GB。
場景 2:內存分配常量(由 GCeasy 繪制)
上圖顯示了場景 2 JVM 在其生命周期內分配的內存。你可以看到沒有波動。內存是在啟動期間從操作系統保留的,從那時起,沒有波動。無論應用程序中的活動如何,它始終保持在24GB。此行為有可能在一定程度上提高應用程序的性能。
GC 暫停時間影響
當垃圾回收運行時,它會暫停應用程序,這將對客戶產生負面影響。我們使用 GCeasy 工具研究了兩種方案的垃圾回收性能。結果如下:
垃圾回收性能結果
我們注意到 GC 吞吐量和 GC 暫停時間略有下降。在方案 1 中,GC 吞吐量為 96.59%,而在方案 2 中,GC 吞吐量略好 (97.83%)。同樣,在場景 1 中,Max GC 的暫停時間為 5.23 秒,而在場景 2 中僅為 1.65 秒。。
應用程序啟動時間
如果將初始堆大小設置為與最大堆大小相同,則應用程序的啟動時間也會更好。以下是 Oracle 文檔的摘錄:
“如果初始堆太小,Java 應用程序的啟動速度會變慢,因為 JVM 被迫頻繁地執行垃圾回收,直到堆增長到更合理的大小。為獲得最佳啟動性能,請將初始堆大小設置為與最大堆大小相同。"
4. 成本
無論您將初始堆大小 (-Xms) 和最大堆大小 (-Xmx) 設置為相同值還是其他值,您支付給雲托管提供商的計算成本都不會更改。假設您正在使用阿里雲、騰訊雲等雲廠商的實例,那么無論設置初始堆大小和最大堆大小的值如何,您最終都將支付固定小時的費用。雲提供商不會根據您在該計算機中使用的內存量向您收費。它們僅根據您使用實例的時間收費。因此,將初始堆大小設置為低於最大堆大小不會節省成本。
結論
在配置線程池或連接池時,將初始堆大小配置為小於最大堆大小是有意義的。在這些資源中,過度分配會產生不必要的影響,但是,內存並非如此。因此,如果要構建企業應用程序,強烈建議將初始堆大小和最大堆大小設置為相同的值。
微信公眾號:Java大家族