故障重現(內存篇2),JAVA內存不足致使頻繁回收和swap引發的性能問題

背景原由:java

記起之前的另外一次也是關於內存的調優分享下數據庫

有個系統平時運行很是穩定運行(沒經歷過大併發考驗),然而在一次活動後,人數併發一上來後,系統開始卡。apache

我按經驗開始調優,在每一個關鍵步驟的加入以下代碼耗時統計進行壓測:ubuntu

long startTime = System.currentTimeMillis();
callRpc();   //這裏好比調用RPC僞代碼,固然還在插入數據庫,中間件地方都加入統計
long costTime = (System.currentTimeMillis() - startTime); 
//統計600毫秒以上耗時
if (costTime > 600) { 
logger.warning("callRpc cost time:" + costTime); 
}

而後去grep日誌, 最後神奇的發現各個地方都有超過600毫秒的地方...tomcat

而後各類定位的誤導...併發

固然最終是解決了,緣由是因爲程序裏使用了大對象致使app

細分析,即便這種狀況深研究也是分不少狀況的性能

 

問題重現:線程

緣由分析:日誌

因爲系統中使用了大對象,當併發來臨,內存講被吃緊,將有可能引發以下三種狀況

第一種狀況, 系統內存夠用(JVM內存未使用到SWAP內存),但JVM內存不夠,最終致使JVM的頻繁垃圾回收(FGC),嚴重影響性能 (stop the word)

第二種狀況,系統內存不夠,把JVM堆部分用到了SWAP,那麼此時的垃圾回收須要把SWAP的內存換回到系統物理內存再進行JVM的垃圾回收。最大影響,致使每次GC的時間變得好久

第三種狀況,  物理內存不夠用, 大量JVM的堆內存被交換到SWAP後,垃圾回收時,把SWAP內存換回物理內存,但SWAP的內存又不會當即回, 此時能夠觀察到垃圾回收同時swap使用的內存會變大(其它部份內存要交換到SWAP裏)

 

準備:

ubuntu 1G  4核

先關閉SWAP虛擬空間 sudo swapoff -a

java version "1.7.0_101"

apache-tomcat-8.5.9

設置好Tomcat的JVM內存:

JAVA_OPTS="-Xmx500m -Xms500m -Xmn200m -Xss228k -XX:+UseConcMarkSweepGC -XX:+UseParNewGC"

apache-jmeter-2.13 用於模擬HTTP請求壓測,都是100條線程併發進行壓測

 

模擬代碼以下

/**
 * 模擬當系統中使用大對象時,對JVM形成的影響
 * 
 * @author 包子(何錦彬). 2017.01.07
 * @QQ 277803242
 */
@WebServlet("/Test")
public class Test extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private Logger logger = Logger.getLogger(Test.class.getName());

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // use java heap 10m
        byte[] bts = new byte[1024 * 1024 * 10];// 代碼段1
        long startTime = System.currentTimeMillis();
        // deal
        try {
            // 模擬業務花費時間
            Thread.sleep(500);
        } catch (InterruptedException e) {
        }

        // 理論上這裏輸出500附近
        long costTime = (System.currentTimeMillis() - startTime);
        if (costTime > 600) {
            logger.warning("cost time:" + costTime);
        }

        Writer out = response.getWriter();
        out.append("ok");
    }

 

 

先模擬正常的狀況:

先註釋掉"代碼段1", 1W個請求下來,基本耗時都在500,一切正常,返回都是在500毫秒附近

垃圾回收狀況,只發生了1次YGC,因此係統正常穩定...

 

模擬第一種狀況:

放開「代碼段1」,讓每次請求都去堆內存申請10m的堆空間,一樣是1W個請求,返回的平均值已經接近了2S

垃圾回收狀況來看, 已經發生了1966次的FGC了, 在上面耗時158秒

 

模擬第二種狀況:

把系統的SWAP打開,打開2G的SWAP,swapon -a

調大JVM參數,到1G,讓JVM用到部分SWAP的空間

此時再讓每次請求都去堆內存申請10m的堆空間,一樣是1W個請求,性能已經低於第二種狀況

垃圾回收來看,回收次數是1766次,比第二種狀況發生的次數還要少,但FGC耗費的時間已是212秒,

(雖然多了200m內存也沒差距這麼大,更精確看年輕代同樣的內存,耗時也遠超過)遠超過第二種狀況了

此時垃圾回收詳情:

模擬第三種狀況:

這種狀況和我上一篇提到的相似,若是系統內存不夠用時,系統將KIIL掉內存

 

總結:

頻繁垃圾回收(FGC),會嚴重影響性能。若是此時用耗時統計去尋找瓶頸,會出現失誤

 

如JVM堆用到了SWAP分區,當發生GC:

1,會致使大量SWAP被使用,2,致使每次GC的時間變得好久;

 

SWAP分區開啓能夠有效防止進程由於內存問題而被系統殺掉;

 

 

持續更新留言問題,解答疑問

歡迎關注個人公衆號,專一重現各類線上的BUG

 或搜 「包子的實驗室」

相關文章
相關標籤/搜索