長時間的GC暫停對於應用程式而言是不可取的。它會影響您的SLA,導致不良的客戶體驗,並對關鍵任務應用程式造成嚴重損害。因此,在本文中,我列出了可能導致長時間GC暫停的主要原因,以及解決這些問題的潛在解決方案。
1.高對象創造率
如果您的應用程式的對象創建率很高,那麼為了跟上它,垃圾回收率也將很高。高垃圾回收率也會增加GC暫停時間。因此,優化應用程式以創建更少的對象是減少長時間GC暫停的有效策略。這可能是一個耗時的練習,但值得100%進行。為了優化應用程式中的對象創建速率,可以考慮使用Java Profiler(如 JProfiler, YourKit或JVisualVM)。這些分析器將報告:
·
創建了哪些對象?
這些對象的創建速率是多少?
它們在內存中佔用多少空間?
誰在創造它們?
始終嘗試優化佔用最大內存量的對象。追捕池塘裡的大魚。
.年齡不足的年輕一代
當年輕一代過小時,對象會過早地提升到老一代。從年輕一代收集垃圾比從年輕一代收集垃圾要花費更多的時間。因此,增加年輕代的大小有可能減少長時間的GC暫停。可以通過設置兩個JVM參數之一來增加年輕一代的大小
-Xmn:指定年輕代的大小。
-XX:NewRatio:指定年輕一代相對於老一代的大小。例如,設置-XX:NewRatio = 2表示老一代與年輕一代之間的比率為1:2。年輕一代將是整個堆的一半。因此,如果堆大小為2 GB,則年輕代大小將為1 GB。
3. GC算法的選擇
GC算法對GC暫停時間有很大影響。如果您是GC專家或打算成為一名GC專家(或者您團隊中的某人是GC專家),則可以調整GC設置以獲得最佳的GC暫停時間。如果您沒有很多GC專業知識,那麼我建議您使用G1 GC算法,因為它具有 自動調整功能。在G1 GC中,您可以使用系統屬性「 -XX:MaxGCPauseMillis」設置GC暫停時間目標。例:
- XX:MaxGCPauseMillis = 200
根據上面的示例,最大GC暫停時間設置為200毫秒。這是一個軟目標,JVM將盡力實現這一目標。
4.交換流程
有時由於缺少內存(RAM),作業系統可能會從內存中交換應用程式。交換非常昂貴,因為它需要磁碟訪問,與物理內存訪問相比要慢得多。以我的拙見,在生產環境中沒有認真的應用可以互換。交換過程時,GC將花費很長時間才能完成。
下面是從StackOverflow獲得的腳本 (感謝作者),該腳本 在執行時將顯示所有正在交換的進程。請確保您的進程沒有被交換。
#!/bin/bash
# Get current swap usage for all running processes
# Erik Ljungstrom 27/05/2011
# Modified by Mikko Rantalainen 2012-08-09
# Pipe the output to "sort -nk3" to get sorted output
# Modified by Marc Methot 2014-09-18
# removed the need for sudo
SUM=0
OVERALL=0
for DIR in `find /proc/ -maxdepth 1 -type d -regex "^/proc/[0-9]+"`
do
PID=`echo $DIR | cut -d / -f 3`
PROGNAME=`ps -p $PID -o comm --no-headers`
for SWAP in `grep VmSwap $DIR/status 2>/dev/null | awk '{ print $2 }'`
do
let SUM=$SUM+$SWAP
done
if (( $SUM > 0 )); then
echo "PID=$PID swapped $SUM KB ($PROGNAME)"
fi
let OVERALL=$OVERALL+$SUM
SUM=0
done
echo "Overall swap used: $OVERALL KB"
如果發現進程正在交換,請執行以下一項操作:
為服務分配更多的RAM。
減少伺服器上運行的進程數,以便它可以釋放內存(RAM)。
減少應用程式的堆大小(我不建議這樣做,因為它可能導致其他副作用。但是,它有可能解決您的問題)。
5.調整GC線程數
對於GC日誌中報告的每個GC事件,將列印用戶,sys和實時。例:
[ 時間:用戶= 25.56 sys = 0.35,實際= 20.48 秒 ]
要了解這些時間之間的區別,請 閱讀本文。(我強烈建議您在繼續本節之前先閱讀本文)。如果在GC事件中,您始終注意到「實時」時間並不比「用戶」時間顯著少,則可能表明GC線程不足。考慮增加GC線程數。假設「用戶」時間為25秒,並且您已將GC線程數配置為5,那麼實際時間應接近5秒(因為25秒/ 5個線程= 5秒)。
警告:添加過多的GC線程將消耗大量CPU,並佔用應用程式資源。因此,您需要在增加GC線程數之前進行徹底的測試。
6.後臺IO流量
如果文件系統的I / O活動繁重(即發生大量讀寫操作),這也可能導致長時間的GC暫停。這種繁重的文件系統I / O活動可能不是由您的應用程式引起的。可能是由同一臺伺服器上運行的另一個進程引起的。仍然可能導致您的應用程式遭受長時間的GC暫停。這是LinkedIn工程師的精彩 文章 ,詳細介紹了此問題。
當繁瑣的I / O活動時,您會注意到「實時」時間明顯高於「用戶」時間。例:
[ 時間:用戶= 0.20 sys = 0.01,實際= 18.45 秒 ]
發生這種情況時,以下是一些可能的解決方案:
如果您的應用程式引起了很高的I / O活動,請對其進行優化。
消除導致伺服器上大量I / O活動的進程。
將您的應用程式移到I / O活動較少的其他伺服器上。
提示:如何監視I / O活動
您可以在Unix中使用sar (系統活動報告) 監視I / O活動 。例:
sar -d -p 1
上面的命令每1秒報告一次對設備的讀取/秒和寫入/秒。有關「 sar」命令的更多詳細信息,請參考 本教程。
7. System.gc()調用
當 System.gc()的 或 調用Runtime.getRuntime()。GC() 方法調用被調用時,它會導致停止的世界充滿選區。在世界各地的完整GC期間,整個JVM被凍結(即,在此期間將不執行任何用戶活動)。從以下來源之一進行System.gc()調用:
1. 您自己的開發人員可能會顯式調用System.gc()方法。
2. 它可能是第三方庫,框架,有時甚至是您使用的應用程式伺服器。任何這些都可能調用System.gc()方法。
3. 可以通過使用JMX從外部工具(如VisualVM)觸發。
4. 如果您的應用程式正在使用RMI,則RMI會定期調用System.gc()。可以使用以下系統屬性來配置此間隔:
-Dsun.rmi.dgc.server.gcInterval=n
-Dsun.rmi.dgc.client.gcInterval=n
評估是否絕對需要顯式調用System.gc()。如果不需要,請刪除它。另一方面,可以通過傳遞JVM參數來強制禁用System.gc()調用:-XX:+ DisableExplicitGC。
最後,開發這麼多年我也總結了一套學習Java的資料與面試題,如果你在技術上面想提升自己的話,可以關注我,私信發送領取資料或者在評論區留下自己的聯繫方式,有時間記得幫我點下轉發讓跟多的人看到哦。