JVM調優(三)程式程式碼調優
根據第一節所講的基礎知識,我們根據線上不同的異常情況做程式的優化。
CPU佔用高
us高
根據之前的分析,CPU us高的原因主要是執行執行緒無任何掛起動作,且一直執行,導致CPU沒有機會去排程執行其他的執行緒,造成執行緒餓死的現象。對於這種情況,常見的一種優化方法是對這種執行緒的動作增加Thread.sleep,以釋放CPU的執行權,降低CPU的消耗。對於執行緒要不斷掃描某種狀態,達到自己的可繼續執行下去的條件時再執行(像程式碼while(xxx==yyy)),使用wait/notifyAll會比較好。
sy高
CPU sy高的原因主要是執行緒的執行狀態要經常切換,
另處一個原因是鎖競爭激烈,造成執行緒狀態切換。
方案:最簡單的優化方法是減少執行緒數。若系統要支援大量併發的話,最好建立執行緒佇列來緩解壓力。
注意:降低執行緒數後,可能造成sy降低,us升高。
若無法降低執行緒數,可採用協程,以支援更高併發量。粒度上協程比執行緒小,執行緒不用頻繁切換。(協程在java裡還不太成熟,但是可以大膽往這方面嘗試。)
檔案IO消耗嚴重
從程式角度而言,造成檔案IO消耗嚴重的原因主要是多個執行緒將大量的資料寫到同一檔案,導致檔案很快變得很大,從而寫入速度越來越慢,並造成各執行緒激烈爭搶檔案鎖。
方案:
非同步寫檔案,避免寫入慢導致應用效能下降太多。(如日誌採用log4j的AsyncAppender)
批量讀寫
限流,將讀寫IO降低到一個能接受的範圍。(時間窗,停牌桶等)。
限制檔案大小,大檔案讀或者追加寫動作會增加耗時,限制檔案大小後,超過一定值新生成檔案。
網路IO消耗
方案:限流,控制傳送頻次。
記憶體消耗
釋放不必要的引用。典型如ThreadLocal,線上程結束時,這裡面的內容不會主動釋放,會造成記憶體增大。檢視!ThreadLocal使用不當後果
使用物件快取池建立物件的例項要耗費一定的CPU以及記憶體,使用物件快取池一定程度上可降低JVM Heap記憶體的使用。(慎用,使用不當會造成記憶體增大)。但是引入合理的快取失效機制,會改善記憶體佔用。如FIFO,LRU,LFU。
LRU是最近最少使用頁面置換演算法(Least Recently Used),也就是首先淘汰最長時間未被使用的頁面。LFU是最近最不常用頁面置換演算法(Least Frequently Used),也就是淘汰一定時期內被訪問次數最少的頁。
合理使用SoftReference和WeakReference對於佔據記憶體但又不是必須存在的物件,例如快取物件,也可以基於SoftReference或WeakReference的方式來進行快取。