国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

JVM內存大對象監控和優化問題描述及解決辦法

OSC開源社區 ? 來源:vivo互聯網技術 ? 2023-08-28 11:39 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

服務器內存問題是影響應用程序性能和穩定性的重要因素之一,需要及時排查和優化。本文介紹了某核心服務內存問題排查與解決過程。首先在JVM與大對象優化上進行了有效的實踐,其次在故障轉移與大對象監控上提出了可靠的落地方案。最后,總結了內存優化需要考慮的其他問題。

一、問題描述

音樂業務中,core服務主要提供歌曲、歌手等元數據與用戶資產查詢。隨著元數據與用戶資產查詢量的增長,一些JVM內存問題也逐漸顯露,例如GC頻繁、耗時長,在高峰期RPC調用超時等問題,導致業務核心功能受損。

9ccc3132-4337-11ee-a2ef-92fbcf53809c.png

圖1 業務異常數量變化

二、分析與解決

通過對日志,機器CPU、內存等監控數據分析發現:

YGC平均每分鐘次數12次,峰值為24次,平均每次的耗時在327毫秒。FGC平均每10分鐘0.08次,峰值1次,平均耗時30秒。可以看到GC問題較為突出。

在問題期間,機器的CPU并沒有明顯的變化,但是堆內存出現較大異常。圖2,黃色圓圈處,內存使用急速上升,FGC變的頻繁,釋放的內存越來越少。

9cd45902-4337-11ee-a2ef-92fbcf53809c.png

圖2 老年代內存使用異常

因此,我們認為業務功能異常是機器的內存問題導致的,需要對服務的內存做一次專項優化。

步驟1 JVM優化

以下是默認的JVM參數:

-Xms4096M-Xmx4096M-Xmn1024M-XX:MetaspaceSize=256M-Djava.security.egd=file:/dev/./urandom-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/data/{runuser}/logs/other

如果不指定垃圾收集器,那么JDK 8默認采用的是Parallel Scavenge(新生代) +Parallel Old(老年代),這種組合在多核CPU上充分利用多線程并行的優勢,提高垃圾回收的效率和吞吐量。但是,由于采用多線程并行方式,會造成一定的停頓時間,不適合對響應時間要求較高的應用程序。然而,core這類的服務特點是對象數量多,生命周期短。在系統特點上,吞吐量較低,要求時延低。因此,默認的JVM參數并不適合core服務。

根據業務的特點和多次對照實驗,選擇了如下參數進行JVM優化(4核8G的機器)。該參數將young區設為原來的1.5倍,減少了進入老年代的對象數量。將垃圾回收器換成ParNew+CMS,可以減少YGC的次數,降低停頓時間。此外還開啟了CMSScavengeBeforeRemark,在CMS的重新標記階段進行一次YGC,以減少重新標記的時間。

-Xms4096M-Xmx4096M-Xmn1536M-XX:MetaspaceSize=256M-XX:+UseConcMarkSweepGC-XX:+CMSScavengeBeforeRemark-Djava.security.egd=file:/dev/./urandom-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/data/{runuser}/logs/other

9ce03704-4337-11ee-a2ef-92fbcf53809c.png

圖3 JVM優化前后的堆內存對比

優化后效果如圖3,堆內存的使用明顯降低,但是Dubbo超時仍然存在。

我們推斷,在業務高峰期,該節點出現了大對象晉升到了老年代,導致內存使用迅速上升,并且大對象沒有被及時回收。那如何找到這個大對象及其產生的原因呢?為了降低問題排查期間業務的損失,提出了臨時的故障轉移策略,盡量降低異常數量。

步驟2 故障轉移策略

在api服務調用core服務出現異常時,將出現異常的機器ip上報給監控平臺。然后利用監控平臺的統計與告警能力,配置相應的告警規則與回調函數。當異常觸發告警,通過配置的回調函數將告警ip傳遞給api服務,此時api服務可以將core服務下的該ip對應的機器視為“故障”,進而通過自定義的故障轉移策略(實現Dubbo的AbstractLoadBalance抽象類,并且配置在項目),自動將該ip從提供者集群中剔除,從而達到不去調用問題機器。圖 4 是整個措施的流程。在該措施上線前,每當有機器內存告警時,將會人工重啟該機器。

9d09b1ba-4337-11ee-a2ef-92fbcf53809c.jpg

圖4 故障轉移策略

步驟3 大對象優化

大對象占用了較多的內存,導致內存空間無法被有效利用,甚至造成OOM(Out Of Memory)異常。在優化過程中,先是查看了異常期間的線程信息,然后對堆內存進行了分析,最終確定了大對象身份以及產生的接口

(1) Dump Stack 查看線程

從監控平臺上Dump Stack文件,發現一定數量的如下線程調用。

Thread 5612: (state = IN_JAVA)
 - org.apache.dubbo.remoting.exchange.codec.ExchangeCodec.encodeResponse(org.apache.dubbo.remoting.Channel, org.apache.dubbo.remoting.buffer.ChannelBuffer, org.apache.dubbo.remoting.exchange.Response) @bci=11, line=282 (Compiled frame; information may be imprecise)
 - org.apache.dubbo.remoting.exchange.codec.ExchangeCodec.encode(org.apache.dubbo.remoting.Channel, org.apache.dubbo.remoting.buffer.ChannelBuffer, java.lang.Object) @bci=34, line=73 (Compiled frame)
 - org.apache.dubbo.rpc.protocol.dubbo.DubboCountCodec.encode(org.apache.dubbo.remoting.Channel, org.apache.dubbo.remoting.buffer.ChannelBuffer, java.lang.Object) @bci=7, line=40 (Compiled frame)
 - org.apache.dubbo.remoting.transport.netty4.NettyCodecAdapter$InternalEncoder.encode(io.netty.channel.ChannelHandlerContext, java.lang.Object, io.netty.buffer.ByteBuf) @bci=51, line=69 (Compiled frame)
 - io.netty.handler.codec.MessageToByteEncoder.write(io.netty.channel.ChannelHandlerContext, java.lang.Object, io.netty.channel.ChannelPromise) @bci=33, line=107 (Compiled frame)
 - io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(java.lang.Object, io.netty.channel.ChannelPromise) @bci=10, line=717 (Compiled frame)
 - io.netty.channel.AbstractChannelHandlerContext.invokeWrite(java.lang.Object, io.netty.channel.ChannelPromise) @bci=10, line=709 (Compiled frame)
...

state = IN_JAVA 表示Java虛擬機正在執行Java程序。從線程調用信息可以看到,Dubbo正在調用Netty,將輸出寫入到緩沖區。此時的響應可能是一個大對象,因而在對響應進行編碼、寫緩沖區時,需要耗費較長的時間,導致抓取到的此類線程較多。另外耗時長,也即是大對象存活時間長,導致full gc 釋放的內存越來越小,空閑的堆內存變小,這又會加劇full gc 次數。

這一系列的連鎖反應與圖2相吻合,那么接下來的任務就是找到這個大對象。

(2)Dump Heap 查看內存

對core服務的堆內存進行了多次查看,其中比較有代表性的一次快照的大對象列表如下,

9d229766-4337-11ee-a2ef-92fbcf53809c.png

圖5 core服務的堆內存快照

整個Netty的taskQueue有258MB。并且從圖中綠色方框處可以發現,單個的Response竟達到了9M,紅色方框處,顯示了調用方的服務名以及URI。

進一步排查,發現該接口會通過core服務查詢大量信息,至此基本排查清楚了大對象的身份以及產生原因。

(3)優化結果

在對接口進行優化后,整個core服務也出現了非常明顯的改進。YGC全天總次數降低了76.5%,高峰期累計耗時降低了75.5%。FGC三天才會發生一次,并且高峰期累計耗時降低了90.1%。

9d2cff62-4337-11ee-a2ef-92fbcf53809c.png

圖6大對象優化后的core服務GC情況

盡管優化后,因內部異常導致獲取核心業務失敗的異常請求數顯著減少,但是依然存在。為了找到最后這一點異常產生的原因,我們打算對core服務內存中的對象大小進行監控。

9d4ae2c0-4337-11ee-a2ef-92fbcf53809c.png

圖7系統內部異常導致核心業務失敗的異常請求數

步驟4 無侵入式內存對象監控

Debug Dubbo 源碼的過程中,發現在網絡層,Dubbo通過encodeResponse方法對響應進行編碼并寫入緩沖區,通過checkPayload方法去檢查響應的大小,當超過payload時,會拋出ExceedPayloadLimitException異常。在外層對異常進行了捕獲,重置buffer位置,而且如果是ExceedPayloadLimitException異常,重新發送一個空響應,這里需要注意,空響應沒有原始的響應結果信息,源碼如下。

//org.apache.dubbo.remoting.exchange.codec.ExchangeCodec#encodeResponse
protected void encodeResponse(Channel channel, ChannelBuffer buffer, Response res) throws IOException {
    //...省略部分代碼
    try {
 
        //1、檢查響應大小是否超過 payload,如果超過,則拋出ExceedPayloadLimitException異常
        checkPayload(channel, len);
 
 
    } catch (Throwable t) {
         
        //2、重置buffer
        buffer.writerIndex(savedWriteIndex);
 
        //3、捕獲異常后,生成一個新的空響應
        Response r = new Response(res.getId(), res.getVersion());
        r.setStatus(Response.BAD_RESPONSE);
         
        //4、ExceedPayloadLimitException異常,將生成的空響應重新發送一遍
        if (t instanceof ExceedPayloadLimitException) {
            r.setErrorMessage(t.getMessage());
            channel.send(r);
            return;
        }
         
    }
}
 
//org.apache.dubbo.remoting.transport.AbstractCodec#checkPayload
protected static void checkPayload(Channel channel, long size) throws IOException {
    int payload = getPayload(channel);
    boolean overPayload = isOverPayload(payload, size);
    if (overPayload) {
        ExceedPayloadLimitException e = new ExceedPayloadLimitException("Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel);
        logger.error(e);
        throw e;
    }
}

受此啟發,自定義了編解碼類(實現org.apache.dubbo.remoting.Codec2接口,并且配置在項目),去監控超出閾值的對象,并打印請求的詳細信息,方便排查問題。在具體實現中,如果特意去計算每個對象的大小,那么勢必是對服務性能造成影響。經過分析,采取了和checkPayload一樣的方式,根據編碼前后buffer的writerIndex位置去判斷有沒有超過設定的閾值。代碼如下。

/**
 * 自定義dubbo編碼類
 **/
public class MusicDubboCountCodec implements Codec2 {
 
    /**
     * 異常響應池:緩存超過payload大小的responseId
     */
    private static Cache EXCEED_PAYLOAD_LIMIT_CACHE = Caffeine.newBuilder()
        // 緩存總條數
        .maximumSize(100)
        // 過期時間
        .expireAfterWrite(300, TimeUnit.SECONDS)
        // 將value設置為軟引用,在OOM前直接淘汰
        .softValues()
        .build();
 
 
    @Override
    public void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException {
        //1、記錄數據編碼前的buffer位置
        int writeBefore = null == buffer ? 0 : buffer.writerIndex();
 
        //2、調用原始的編碼方法
        dubboCountCodec.encode(channel, buffer, message);
 
        //3、檢查&記錄超過payload的信息
        checkOverPayload(message);
 
        //4、計算對象長度
        int writeAfter = null == buffer ? 0 : buffer.writerIndex();    
        int length = writeAfter - writeBefore;
 
        //5、超過告警閾值,進行日志打印處理
        warningLengthTooLong(length, message);
    }
 
    //校驗response是否超過payload,超過了,緩存id
    private void checkOverPayload(Object message){
        if(!(message instanceof Response)){
            return;
        }
        Response response = (Response) message;
 
        //3.1、新的發送過程:通過狀態碼BAD_RESPONSE與錯誤信息識別出空響應,并記錄響應id
        if(Response.BAD_RESPONSE == response.getStatus() && StrUtil.contains(response.getErrorMessage(), OVER_PAYLOAD_ERROR_MESSAGE)){          
            EXCEED_PAYLOAD_LIMIT_CACHE.put(response.getId(), response.getErrorMessage());
            return;
        }
 
        //3.2、原先的發送過程:通過異常池識別出超過payload的響應,打印有用的信息
        if(Response.OK == response.getStatus() &&  EXCEED_PAYLOAD_LIMIT_CACHE.getIfPresent(response.getId()) != null){      
            String responseMessage = getResponseMessage(response);
            log.warn("dubbo序列化對象大小超過payload,errorMsg is {},response is {}", EXCEED_PAYLOAD_LIMIT_CACHE.getIfPresent(response.getId()),responseMessage);
        }
    }
     
}

在上文中提到,當捕獲到超過payload的異常時,會重新生成空響應,導致失去了原始的響應結果,此時再去打印日志,是無法獲取到調用方法和入參的,但是encodeResponse方法步驟4中,重新發送這個Response,給了我們機會去獲取到想要的信息,因為重新發送意味著會再去走一遍自定義的編碼類。

假設有一個超出payload的請求,執行到自定編碼類encode方法的步驟2(Dubbo源碼中的編碼方法),在這里會調用encodeResponse方法重置buffer,發送新的空響應。

(1)當這個新的空響應再次進入自定義encode方法,執行 checkOverPayload方法的步驟3.1時,就會記錄異常響應的id到本地緩存。由于在encodeResponse中buffer被重置,無法計算對象的大小,所以步驟4、5不會起到實際作用,就此結束新的發送過程。

(2)原先的發送過程回到步驟2 繼續執行,到了步驟3.2 時,發現本地緩存的異常池中有當前的響應id,這時就可以打印調用信息了。

綜上,對于大小在告警閾值和payload之間的對象,由于響應信息成功寫入了buffer,可以直接進行大小判斷,并且打印響應中的關鍵信息;對于超過payload的對象,在重新發送中記錄異常響應id到本地,在原始發送過程中訪問異常id池識別是否是異常響應,進行關鍵信息打印。

在監控措施上線后,通過日志很快速的發現了一部分產生大對象的接口,當前也正在根據接口特點做針對性優化。

三、總結

在對服務JVM內存進行調優時,要充分利用日志、監控工具、堆棧信息等,分析與定位問題。盡量降低問題排查期間的業務損失,引入對象監控手段也不能影響現有業務。除此之外,還可以在定時任務、代碼重構、緩存等方面進行優化。優化服務內存不僅僅是JVM調參,而是一個全方面的持續過程。





審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 編碼器
    +關注

    關注

    45

    文章

    3953

    瀏覽量

    142617
  • RPC
    RPC
    +關注

    關注

    0

    文章

    114

    瀏覽量

    12260
  • cms
    cms
    +關注

    關注

    0

    文章

    60

    瀏覽量

    11674
  • JVM
    JVM
    +關注

    關注

    0

    文章

    161

    瀏覽量

    13036
  • 回調函數
    +關注

    關注

    0

    文章

    95

    瀏覽量

    12195

原文標題:JVM 內存大對象監控和優化實踐

文章出處:【微信號:OSC開源社區,微信公眾號:OSC開源社區】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    內存與數據處理優化藝術

    內存訪問是程序運行的瓶頸之一。減少內存訪問次數可以顯著提高程序的運行速度。 在C語言中,指針是直接操作內存的利器。使用指針遍歷數組不僅代碼更簡潔,而且效率更高。例如,用指針直接訪問內存
    發表于 11-14 07:46

    openocd failed with code (1)的一種解決辦法

    我們發現此錯誤是由于配置中默認文件路徑有誤導致的,在默認模板中,elf文件的路徑中使用的是“/”,而windows系統默認文件路徑是“”,所以導致elf文件無法識別而無法下載。 解決辦法一是手動修改
    發表于 10-27 08:21

    VCS安裝教程及常見問題和解決辦法

    解決辦法。 二、問題提出及相應解決辦法 1、license文件過期問題 大家在網上看到的VCS安裝教程中,都會附上license文件,并且告訴你如何獲取Host Name等信息。 但有時獲取
    發表于 10-27 07:58

    時序約束問題的解決辦法

    slack 計算如下圖所示: 所以 slakc 為負數時,說明路徑的組合邏輯延時過長。解決辦法有兩個:第一個是降低時鐘頻率,第二個是將延時過長的組合邏輯拆成兩個或者多個時鐘周期執行。 無論 Setup
    發表于 10-24 09:55

    單向閥氣密性檢測儀常見故障及解決辦法

    單向閥氣密性檢測儀在工業生產中起著至關重要的作用,然而在使用過程中難免會出現一些故障。了解常見故障及其解決辦法,能有效提高設備的使用效率和檢測準確性。一、檢測結果不準確故障表現檢測數據波動
    的頭像 發表于 06-30 14:01 ?602次閱讀
    單向閥氣密性檢測儀常見故障及<b class='flag-5'>解決辦法</b>

    請問如何優化OpenVINO?工具套件中的內存使用?

    運行OpenVINO?推斷時找不到優化內存使用情況的方法。
    發表于 06-25 06:56

    鴻蒙5開發寶藏案例分享---內存優化實戰指南

    ≈ 4.6MB)</span>: 這是你的 ArkTS 代碼(主要是 JS/TS 對象)占用的堆內存。這是優化的 主戰場 ! <span class
    發表于 06-12 17:15

    HarmonyOS優化應用內存占用問題性能優化

    一、使用purgeable優化C++內存 Purgeable Memory是HarmonyOS中native層常用的內存管理機制,可用于圖像處理的Bitmap、流媒體應用的一次性數據、圖片等
    發表于 05-24 17:20

    HarmonyOS優化應用內存占用問題性能優化

    可以通過管理對象的生命周期來釋放資源、銷毀對象優化ArkTS內存。 在UIAbility組件生命周期中,調用對應生命周期的方法,創建或銷毀資源。如在Create或Foreground
    發表于 05-23 15:35

    HarmonyOS優化應用內存占用問題性能優化

    應用開發過程中注重內存管理,積極采取措施來減少內存占用,以優化應用程序的性能和用戶體驗。 HarmonyOS提供了一些內存管理的工具和接口,幫助開發者有效地管理
    發表于 05-21 11:27

    錫膏印刷機總出問題?老工程師總結 7 大常見問題及解決辦法

    本文分享錫膏印刷機常見 7 大不良及解決辦法:高頻問題包括塌陷(壓力 / 黏度 / 錫粉)、偏位(PCB 固定 / 鋼網精度)、漏印(速度 / 黏度 / 開孔)、凹陷(壓力 / 清潔),補充橋連
    的頭像 發表于 04-15 08:51 ?1716次閱讀
    錫膏印刷機總出問題?老工程師總結 7 大常見問題及<b class='flag-5'>解決辦法</b>

    工程繪圖CAD使用中常見的問題

    CAD(計算機輔助設計)在使用過程中,用戶可能會遇到多種常見問題。以下是一些常見的問題及其解決辦法: 一、軟件操作問題 1. Ctrl+N無效 ? ?● 問題描述:Ctrl+N通常是新建命令,但有
    的頭像 發表于 04-14 15:02 ?1134次閱讀

    存儲示波器的觸發問題及解決辦法

    解決辦法與操作指南1. 無法觸發 檢查觸發閾值: 調整閾值至信號幅度的50%-70%(例如,信號幅度為2V,則設置閾值為1V)。 選擇匹配的觸發模式: 簡單信號用邊沿觸發;復雜信號用脈寬觸發或邏輯
    發表于 04-09 14:39

    GPS北斗定位模塊問題及解決辦法

    GPS北斗定位模塊使用上大多需要配置和設置下的,因此出現應用方面的問題也是可以理解的。以下是常見的問題及其解決辦法: 一、搜不到信號 問題描述: 在家或個別位置無法接收到GPS或北斗定位模塊的信號
    的頭像 發表于 03-30 07:37 ?3433次閱讀

    NA611系列WiFi串口服務器常見問題以及解決辦法

    802.11 a/b/g/n 標準。WiFi串口服務器在連接、配置和使用過程中可能會遇到多種問題。以下是一些常見問題及其解決辦法
    的頭像 發表于 03-17 11:25 ?1016次閱讀