根據第一節所講的基礎知識,我們根據線上不同的異常情況做程序的優化。
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的方式來進行緩存。