微信公眾號: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大家族