記一次Netty堆外內(nèi)存泄漏的排查總結(jié)

背景

今年上半年接手了一位離職同事負(fù)責(zé)的推薦項(xiàng)目赡译,主要是圍繞智能推薦服務(wù)相關(guān)的內(nèi)容湃密,包括了離線蒋失、實(shí)時(shí)數(shù)據(jù)處理和線上的預(yù)測(cè)服務(wù)领舰。

這里的堆外內(nèi)存泄漏也是發(fā)生在預(yù)測(cè)服務(wù)這一塊夫嗓,大概的表現(xiàn)情況就是線上服務(wù)運(yùn)行一段時(shí)間后,客戶端會(huì)發(fā)生大面積connect reset異常冲秽,并且在較短時(shí)間內(nèi)會(huì)發(fā)生雪崩的情況舍咖,下面就簡(jiǎn)單回顧一下整個(gè)問題的排查過程。

環(huán)境

該服務(wù)運(yùn)行在公司內(nèi)部的微服務(wù)框架上锉桑,這套框架比較久遠(yuǎn)排霉,依舊是基于HTTP1.X協(xié)議進(jìn)行通信的,傳輸?shù)男蛄谢玫氖莋oogle的Gson民轴,Server端基于Netty自研了一套郑诺,Client端則是基于okhttp3開發(fā)的。

我們服務(wù)端目前部署了67臺(tái)節(jié)點(diǎn)杉武,下游調(diào)用端超過100臺(tái)節(jié)點(diǎn)辙诞。

公司近期做了雙中心推廣,很多服務(wù)都進(jìn)行了雙中心部署轻抱。

Server節(jié)點(diǎn)配置信息:

8核 13G飞涂,系統(tǒng)版本Centos Linux realease 7.8.2003

服務(wù)性能指標(biāo):

TP99:50ms

TP95:30ms

QPS:2000+(其實(shí)不高)

JVM啟動(dòng)參數(shù):
這里就截取部分參數(shù)了
-Xms10240m -Xmx10240m -XX:NewSize=3072m -XX:MaxNewSize=3072m -XX:MaxDirectMemorySize=1024m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs/skynet-xxx/xxx_heapDump.hprof -XX:+UseParNewGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=1024M -XX:+ExplicitGCInvokesConcurrent -XX:-UseGCOverheadLimit -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=65 -XX:CMSFullGCsBeforeCompaction=2 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:/data/logs/skynet-xxx/xxx_gc.log

請(qǐng)求入?yún)⑸希蛻舳苏?qǐng)求參數(shù)大概在mb級(jí)別祈搜,小點(diǎn)的基本也在數(shù)十kb左右较店,這里入?yún)⑤^大的原因也是業(yè)務(wù)決定的,單次請(qǐng)求下容燕,用戶需要組裝的Item集數(shù)量很大梁呈,并且推薦模型所用到的特征中,實(shí)時(shí)特征部分也大量依賴了實(shí)時(shí)的Item業(yè)務(wù)屬性蘸秘,例如官卡,兩程方案按照用戶請(qǐng)求動(dòng)態(tài)組合所計(jì)算出的特征會(huì)依賴Item的具體多個(gè)業(yè)務(wù)屬性蝗茁,因此入?yún)?bào)文大也就可以理解了。

故障

先來看下當(dāng)天的故障監(jiān)控

8點(diǎn)半開始寻咒,下游調(diào)用方開始大面積訪問異常哮翘,并且出現(xiàn)雪崩的現(xiàn)象,當(dāng)時(shí)9點(diǎn)半收到通知報(bào)警(當(dāng)天正好是周五毛秘,美好的周五準(zhǔn)備晚點(diǎn)到公司饭寺,恰逢高架無限堵車,均速5碼叫挟,不知道大家能體會(huì)筆者當(dāng)時(shí)的心境不艰匙,唉,其實(shí)當(dāng)時(shí)不光滿心都是淚抹恳,三急也是呼之欲出的狀態(tài)旬薯,then,N minutes later......)适秩。

到公司那一刻起,開始定位問題硕舆,調(diào)用方發(fā)現(xiàn)大面積拋connect reset

分析

1 查看日志

首先定位服務(wù)端日志異常秽荞,看下來,并沒有發(fā)現(xiàn)存在可疑異常抚官,采樣了幾臺(tái)節(jié)點(diǎn)扬跋,通過jstack打印了線程棧,看了下凌节,也并沒有看到blocked住的情況钦听。

無奈去查看公共微服務(wù)日志文件夾下的日志,結(jié)果發(fā)現(xiàn)了端倪倍奢,Server端大量拋OOM朴上。

2 問題分析

就OOM來看,是源于Netty堆外內(nèi)存溢出引起的卒煞,再看used:1056964615, max:1073741824痪宰,已用堆外內(nèi)存1056964615=1056964615/1024/1024>1008M,而max=1073741824/1024/1024=1024M畔裕,Netty再向堆外申請(qǐng)16777216=16777216/1024/1024=16M內(nèi)存時(shí)衣撬,明顯就不夠了,因此拋OOM扮饶。

這里回看本文開頭提到的JVM啟動(dòng)參數(shù)也可以對(duì)應(yīng)起來具练,-XX:MaxDirectMemorySize=1024m,剛好max也是1024M甜无。

緊接著扛点,我們就去查看了下Netty具體的溢出判定邏輯哥遮。

先看PlatformDependent類中的incrementMemoryCounter方法:

Netty源碼-PlatformDependent-1

Netty內(nèi)部通過全局的DIRECT_MEMORY_COUNTER變量來統(tǒng)計(jì)應(yīng)用端已經(jīng)使用的堆外內(nèi)存空間,并且DIRECT_MEMORY_COUNTER也被申明為全局靜態(tài)變量占键,在allocateDirectNoCleaner和reallocateDirectNoCleaner會(huì)做compareAndSet(usedMemory, newUsedMemory)增加動(dòng)作昔善,如exception則執(zhí)行decrementMemoryCounter,相當(dāng)于回滾畔乙;在freeDirectNoCleaner也會(huì)進(jìn)行decrementMemoryCounter君仆,歸還已經(jīng)申請(qǐng)的空間。

Netty源碼-PlatformDependent-2

其實(shí)到這里牲距,業(yè)務(wù)訂單流失的壓力已經(jīng)很大了返咱,為了優(yōu)先保障業(yè)務(wù),這里保留了一臺(tái)線上故障節(jié)點(diǎn)的故障現(xiàn)場(chǎng)牍鞠,先將所有故障節(jié)點(diǎn)進(jìn)行重啟咖摹,重啟前留了個(gè)心,將DIRECT_MEMORY_COUNTER變量進(jìn)行了監(jiān)控难述,監(jiān)控部分代碼也同時(shí)上線重啟萤晴,代碼如下:

@Component
public class DirectMemoryReporterImpl {

    private AtomicLong directMemory;

    @PostConstruct
    public void init() {
        Field field = ReflectionUtils.findField(PlatformDependent.class, "DIRECT_MEMORY_COUNTER");
        field.setAccessible(true);

        try {
            directMemory = (AtomicLong) field.get(PlatformDependent.class);
        } catch (IllegalAccessException ignored) {
        }

        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        scheduledExecutorService.scheduleAtFixedRate(this::doReport, 0, 1, TimeUnit.SECONDS);
    }

    private void doReport() {
        try {
            long memory = directMemory.get();
            SkyLogHelper.traceInfo(LogModule.Monitor, "DirectMemoryReporterImpl", "doReport", "DIRECT_MEMORY_COUNTER -> " + memory + "b", "");
        } catch (Exception e) {
            SkyLogHelper.traceError(LogModule.Monitor, "DirectMemoryReporterImpl", "doReport", "e", "", e);
        }
    }
}

重新部署期間,除了觀察線上調(diào)用側(cè)的異常指標(biāo)監(jiān)控外胁后,又想了下店读,為什么會(huì)有16M的內(nèi)存申請(qǐng)呢,16M又是哪來的呢攀芯,然后繼續(xù)按照異常棧一步步跟源碼屯断,到了PoolArena類的newChunk方法,并且這里的chunkSize是由PoolArena類的allocateNormal傳入侣诺,這里能看到DirectArena是PoolArena的實(shí)現(xiàn)殖演,而調(diào)用其構(gòu)造方法的地方則是PooledByteBufAllocator的初始化方法。

PoolArena

Netty源碼-PoolArena-1
Netty源碼-PoolArena-2

PooledByteBufAllocator

Netty源碼-PooledByteBufAllocator-1
Netty源碼-PooledByteBufAllocator-2

validateAndCalculateChunkSize就是計(jì)算Chunk大小的方法年鸳,通過pageSize頁(yè)大小和maxOrder深度來計(jì)算的趴久,在PooledByteBufAllocator內(nèi)部也有兩個(gè)地方有說明,如下:

Netty源碼-PooledByteBufAllocator-3
Netty源碼-PooledByteBufAllocator-4

具體Netty是如何管理PoolChunk的大家可以參考下這篇文章 [支撐百萬(wàn)級(jí)并發(fā)搔确,Netty如何實(shí)現(xiàn)高性能內(nèi)存管理]朋鞍,講的還是比較不錯(cuò)的,這里就不額外展開了妥箕。

我們回到服務(wù)滥酥,接著聊,生產(chǎn)部署完成后畦幢,找了下具體新部署的節(jié)點(diǎn)坎吻,觀察下剛剛上線的DIRECT_MEMORY_COUNTER監(jiān)控,N hours later......

果然宇葱,存在持續(xù)緩慢增長(zhǎng)的內(nèi)存泄漏問題瘦真,由于項(xiàng)目?jī)?nèi)部并沒有存在持續(xù)的基于Netty的IO操作刊头,因此將懷疑點(diǎn)下沉到底層組件。

復(fù)現(xiàn)

這里筆者將所有的業(yè)務(wù)邏輯代碼全部注掉诸尽,只保留微服務(wù)協(xié)議接口原杂,方法內(nèi)部只做了一個(gè)Thread.sleep(50);然后返回結(jié)果,并將代碼在線下本地部署您机;sdk端使用公司client進(jìn)行了兩輪壓測(cè)穿肄,壓測(cè)邏輯分別為串行1000次超時(shí)200ms的調(diào)用和串行1000次1ms超時(shí)的調(diào)用(這里僅僅只是為了模擬成功和失敗的兩種場(chǎng)景);請(qǐng)求入?yún)⑹褂昧艘粋€(gè)800kb的線上業(yè)務(wù)實(shí)體际看。

這里為了更便于復(fù)現(xiàn)咸产,服務(wù)啟動(dòng)時(shí)啟用了

-Dio.netty.allocator.type=unpooled  使用非Pool池管理
-Dio.netty.leakDetectionLevel=paranoid  啟用Netty堆外內(nèi)存泄漏檢測(cè)工具,級(jí)別=paranoid

有意思的一幕發(fā)生了

1000次200ms超時(shí)調(diào)用:

測(cè)試1

1000次1ms超時(shí)調(diào)用:

測(cè)試2

實(shí)驗(yàn)2直接Netty檢測(cè)內(nèi)存泄漏了仲闽,問題到此復(fù)現(xiàn)成功脑溢。

定位

那么,為什么超時(shí)會(huì)造成堆外內(nèi)存泄漏呢赖欣,這里我們沒有別的辦法屑彻,只能閱讀公司組件源碼,找找原因了顶吮。

首先看下公司服務(wù)端組件Netty相關(guān)的初始化動(dòng)作社牲,它繼承了ChannelInitializer饱亮,并在初始化階段追加了自定義的HttpHandler:

HttpHandler繼承了ChannelInboundHandlerAdapter,并且使用了CompositeByteBuf鸯绿,這里由于涉及到公司內(nèi)部核心組件规个,因此,只能用偽代碼進(jìn)行展示了钢拧,這里我們只截取一些Netty相關(guān)通用代碼邏輯塊:

public class HttpHandler extends ChannelInboundHandlerAdapter {

    private HttpRequest req;

    private final CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer(32);

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof HttpRequest) {
            try {
                req = (HttpRequest) msg;
                context = initReq(ctx, msg, req);

                if (!req.decoderResult().isSuccess()) {
                    WriteUtils.write("請(qǐng)求無效, 解碼失敗", HttpResponseStatus.BAD_REQUEST, context);
                    return;
                }
                。巡揍。。
            } catch (Exception e) {
                WriteUtils.write(e.getMessage(), HttpResponseStatus.INTERNAL_SERVER_ERROR, context);
                return;
            }
        }

        if (msg instanceof HttpContent) {
            HttpContent content = (HttpContent) msg;
            ByteBuf bytebuf = content.content();
            compositeByteBuf.addComponent(true, bytebuf);
            if (msg instanceof LastHttpContent) {
                // 校驗(yàn)請(qǐng)求
                if (!validRequest(ctx, context)) return;
                context.getInvocation().setRequestBody(compositeByteBuf.toString(CharsetUtil.UTF_8));
                compositeByteBuf.clear();
                compositeByteBuf.removeComponents(0, compositeByteBuf.numComponents());
                // 具體業(yè)務(wù)行為傳遞
                buildChainHandler().handle(context);
            }
        }
    }
    
    菌瘪。腮敌。。
}

看到這里俏扩,我們就必須了解下Netty對(duì)于Http協(xié)議的抽象定義了糜工,推薦這篇文章netty對(duì)http協(xié)議解析原理解析,這里就簡(jiǎn)單提下對(duì)于Http的幾種內(nèi)容的包裝:

  • HttpMethod:主要是對(duì)method的封裝录淡,包含method序列化的操作

  • HttpVersion: 對(duì)version的封裝捌木,netty包含1.0和1.1的版本

  • QueryStringDecoder: 主要是對(duì)url進(jìn)行封裝,解析path和url上面的參數(shù)嫉戚。(Tips:在tomcat中如果提交的post請(qǐng)求是application/x-www-form-urlencoded刨裆,則getParameter獲取的是包含url后面和body里面所有的參數(shù)澈圈,而在netty中,獲取的僅僅是url上面的參數(shù))

  • HttpHeaders:包含對(duì)header的內(nèi)容進(jìn)行封裝及操作

  • HttpContent:是對(duì)body進(jìn)行封裝帆啃,本質(zhì)上就是一個(gè)ByteBuf瞬女。如果ByteBuf的長(zhǎng)度是固定的,則請(qǐng)求的body過大努潘,可能包含多個(gè)HttpContent诽偷,其中最后一個(gè)為L(zhǎng)astHttpContent(空的HttpContent),用來說明body的結(jié)束。

  • HttpRequest:主要包含對(duì)Request Line和Header的組合

  • FullHttpRequest: 主要包含對(duì)HttpRequest和httpContent的組合

Netty-Http生命周期

從生命周期上來講慈俯,HttpRequest -> HttpContent ...... -> LastHttpContent渤刃,一個(gè)完整的流程。

公司組件的代碼中贴膘,是將每個(gè)HttpContent類型的msg都放入CompositeByteBuf卖子,當(dāng)?shù)阶詈笠粋€(gè)LastHttpContent到達(dá)時(shí),組裝CompositeByteBuf中已經(jīng)寫入的所有HttpContent刑峡,然后清理CompositeByteBuf中所有的ByteBuf引用洋闽,并進(jìn)行清理(這里的清理也并不是立即執(zhí)行,而是會(huì)等到AbstractReferenceCountedByteBuf中的refCnt下一次變?yōu)?時(shí)觸發(fā)deallocate())突梦,再調(diào)用業(yè)務(wù)方法诫舅,直到ChannelHandler結(jié)束被回收,完成整個(gè)生命周期宫患。

那么刊懈,如果LastHttpContent沒有到來會(huì)怎么樣呢?

其實(shí)這也是timeout=1的那一輪測(cè)試所對(duì)應(yīng)的問題了娃闲,消息體發(fā)送不完整虚汛,這里就會(huì)存在LastHttpContent邏輯塊無法觸達(dá)的情況,也就是CompositeByteBuf所緩存的ByteBuf引用一直被持有皇帮,并且未被執(zhí)行手動(dòng)釋放操作卷哩,那么一直到ChannelHandler生命周期結(jié)束,堆外所開辟的空間都將一直被占用属拾,內(nèi)存泄漏将谊。

到此,我們似乎找到了一個(gè)可疑的內(nèi)存泄漏點(diǎn)渐白,那么如何證明就是它引起的呢尊浓?做法也很簡(jiǎn)單。

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.info("*** channelInactive");
        super.channelInactive(ctx);
        compositeByteBuf.clear();
        compositeByteBuf.removeComponents(0, compositeByteBuf.numComponents());
    }

在Inative階段纯衍,我們?cè)偃ナ謩?dòng)釋放一次CompositeByteBuf眠砾,下面來測(cè)試一下。

測(cè)試3

堆外內(nèi)存泄漏問題順利解決。

到此褒颈,其實(shí)我們已經(jīng)將問題順利定位柒巫,并提交公共組件負(fù)責(zé)團(tuán)隊(duì)進(jìn)行確認(rèn),并著手修復(fù)了谷丸。

那我們?cè)倩叵胂卤ぬ停@個(gè)場(chǎng)景時(shí)線下可以復(fù)現(xiàn)的情況下,我們嘗試通過測(cè)試手段進(jìn)行場(chǎng)景模擬刨疼,然后定位的泉唁,那么如果生產(chǎn)環(huán)境下,我們會(huì)怎么去定位這個(gè)問題呢揩慕,下面再聊聊當(dāng)時(shí)生產(chǎn)環(huán)境戰(zhàn)斗的過程亭畜,也是異常兇險(xiǎn)啊~~

開始下半場(chǎng)正文

由于是Netty堆外溢出,重新部署服務(wù)時(shí)迎卤,筆者保留了一臺(tái)線上故障節(jié)點(diǎn)拴鸵,可供回溯,就從這臺(tái)節(jié)點(diǎn)入手蜗搔。

首先期望可以定位到既然堆外內(nèi)存溢出劲藐,那么當(dāng)時(shí)堆外內(nèi)存泄漏的部分到底是什么內(nèi)容呢?

這里我們查看了服務(wù)進(jìn)行的內(nèi)存映射:

jps -m
pmap -pid
163:   java -Duser.timezone=GMT+08 -server -Xms8192m -Xmx10240m -XX:NewSize=3072m -XX:MaxNewSize=3072m -XX:MaxDirectMemorySize=1024m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m-XX:+UseContainerSupport -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs/xxx/xxx_heapDump.hprof -XX:+UseParNewGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=1024M -XX:+ExplicitGCInv
0000000000400000      4K r-x-- java
0000000000600000      4K r---- java
0000000000601000      4K rw--- java
00000000016c0000    132K rw---   [ anon ]
0000000560800000 8388608K rw---   [ anon ]
0000000760800000 2097152K -----   [ anon ]
00000007e0800000   6528K rw---   [ anon ]
00000007e0e60000 1042048K -----   [ anon ]
00007f91b8000000    892K rw---   [ anon ]
00007f91b80df000  64644K -----   [ anon ]
00007f91bc000000    132K rw---   [ anon ]
00007f91bc021000  65404K -----   [ anon ]
00007f91c0000000    800K rw---   [ anon ]
00007f91c00c8000  64736K -----   [ anon ]
00007f91c4000000    980K rw---   [ anon ]
00007f91c40f5000  64556K -----   [ anon ]
00007f91c8000000    916K rw---   [ anon ]
00007f91c80e5000  64620K -----   [ anon ]
00007f91cc000000    608K rw---   [ anon ]
00007f91cc098000  64928K -----   [ anon ]
00007f91d0000000    932K rw---   [ anon ]
00007f91d00e9000  64604K -----   [ anon ]
00007f91d4000000    584K rw---   [ anon ]
00007f91d4092000  64952K -----   [ anon ]
00007f91d8000000    756K rw---   [ anon ]
00007f91d80bd000  64780K -----   [ anon ]
00007f91dc000000   1452K rw---   [ anon ]
00007f91dc16b000  64084K -----   [ anon ]
00007f91e0000000    612K rw---   [ anon ]
00007f91e0099000  64924K -----   [ anon ]

能看到不少anon的64M左右的連續(xù)空間樟凄,每一組例如892K + 64644K = 65536K 正好是64M聘芜,筆者線下也對(duì)比了堆外內(nèi)存泄漏前后相關(guān)內(nèi)存塊的變化:

發(fā)現(xiàn)所分配的rw內(nèi)存會(huì)持續(xù)增長(zhǎng),那么這里就準(zhǔn)備查看下這一部分變化的內(nèi)存中到底是哪些內(nèi)容缝龄。

這里會(huì)用到gdb調(diào)試工具汰现,如果有c或c++相關(guān)基礎(chǔ)的同學(xué)可以跳過這一部分:

這里附帶一份安裝指令集:

wget http://mirrors.ustc.edu.cn/gnu/gdb/gdb-7.9.1.tar.xz
tar -xf gdb-7.9.1.tar.xz
cd gdb-7.9.1
yum install texinfo
./configure
// 這里可能會(huì)拋異常no termcap library found
// 下載termcap -> https://ftp.gnu.org/gnu/termcap/
mkdir ../termcap
cd ../termcap
wget https://ftp.gnu.org/gnu/termcap/termcap-1.3.1.tar.gz
tar -zxvf termcap-1.3.1.tar.gz
cd termcap-1.3.1
./configure
make
make install
cd ../gdb-7.9.1
make install
gdb -v // 確認(rèn)安裝成功

// 這里如果遇到configure: error: no acceptable C compiler found in $PATH
// 則需要安裝gcc
yum install gcc

N minutes later......

我們終于可以開始使用gdb了,let's do it叔壤。

// 我們就上面分析的那一塊連續(xù)的64M內(nèi)存進(jìn)行dump快照瞎饲,查看具體的內(nèi)容
gdb -p 160
(gdb)dump memory 0x7f91b8000000_0x7f91b80df000.bin 0x7f91b8000000 0x7f91b80df000
(gdb)dump memory 0x7f91b80df000_0x7f91bc000000.bin 0x7f91b80df000 0x7f91bc000000
(gdb)quit
strings 0x7f91b8000000_0x7f91b80df000.bin > 0x7f91b8000000_0x7f91b80df000.log
strings 0x7f91b80df000_0x7f91bc000000.bin > 0x7f91b80df000_0x7f91bc000000.log
less 0x7f91b8000000_0x7f91b80df000.log

終于我們尋根之地,看到了最終的光明圣地百新,

這不正是我們的請(qǐng)求入?yún)⒄拿雌笕恚俏覀冊(cè)?G到最后一行看看庐扫,

果然饭望,非正常中斷,再看第二個(gè)內(nèi)存塊strings后的文件形庭,發(fā)現(xiàn)內(nèi)容為空铅辞,文件大小為0,筆者也在線下模擬內(nèi)存泄漏后進(jìn)行對(duì)比萨醒,發(fā)現(xiàn)持續(xù)的內(nèi)容也是非正常中斷斟珊,對(duì)應(yīng)了我們驗(yàn)證的LastHttpContent未到達(dá),且堆外內(nèi)存未進(jìn)行回收的結(jié)論富纸。

至此囤踩,整個(gè)Netty堆外內(nèi)存泄漏的排查定位過程結(jié)束旨椒。

總結(jié)

其實(shí)這個(gè)問題存在了很長(zhǎng)時(shí)間,至于到近期爆發(fā)堵漱,其實(shí)也是源于公司雙中心機(jī)房升級(jí)引起的综慎,部分下游服務(wù)異地部署,異地服務(wù)需要走專線勤庐,造成異地服務(wù)訪問耗時(shí)加長(zhǎng)示惊,堆外內(nèi)存泄漏的問題才會(huì)被放大。

總的來說愉镰,這次故障定位的過程很艱辛米罚,當(dāng)然,最終的結(jié)果也是很棒的丈探。

筆者在此也算沉淀出一些方法論录择,面對(duì)像OOM這樣的問題時(shí),首先要先明確类嗤,是哪一種OOM糊肠,有堆內(nèi)、堆外遗锣、方法區(qū)等等货裹,也有啟動(dòng)時(shí)階段或者運(yùn)行時(shí)階段,首先要明確當(dāng)下自己的場(chǎng)景精偿,因?yàn)椴粫?huì)有人比你更了解你的代碼弧圆,當(dāng)你堅(jiān)定問題方向后,也要義無反顧的堅(jiān)持下去笔咽,總會(huì)有自己不在行的領(lǐng)域搔预,只要肯多付出時(shí)間、精力叶组,總會(huì)有提高和回報(bào)拯田。

參考文獻(xiàn)

疑案追蹤:Spring Boot內(nèi)存泄露排查記

netty 堆外內(nèi)存泄露排查盛宴

生活不易、各自努力

前路漫漫甩十,互勉同行

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末船庇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子侣监,更是在濱河造成了極大的恐慌鸭轮,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件橄霉,死亡現(xiàn)場(chǎng)離奇詭異窃爷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門按厘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來医吊,“玉大人,你說我怎么就攤上這事逮京≌诳В” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵造虏,是天一觀的道長(zhǎng)御吞。 經(jīng)常有香客問我,道長(zhǎng)漓藕,這世上最難降的妖魔是什么陶珠? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮享钞,結(jié)果婚禮上揍诽,老公的妹妹穿的比我還像新娘。我一直安慰自己栗竖,他們只是感情好暑脆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著狐肢,像睡著了一般添吗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上份名,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天碟联,我揣著相機(jī)與錄音,去河邊找鬼僵腺。 笑死鲤孵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辰如。 我是一名探鬼主播普监,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼琉兜!你這毒婦竟也來了凯正?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤呕童,失蹤者是張志新(化名)和其女友劉穎漆际,沒想到半個(gè)月后淆珊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夺饲,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了往声。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片擂找。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖浩销,靈堂內(nèi)的尸體忽然破棺而出贯涎,到底是詐尸還是另有隱情,我是刑警寧澤慢洋,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布塘雳,位于F島的核電站,受9級(jí)特大地震影響普筹,放射性物質(zhì)發(fā)生泄漏败明。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一太防、第九天 我趴在偏房一處隱蔽的房頂上張望妻顶。 院中可真熱鬧,春花似錦蜒车、人聲如沸讳嘱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沥潭。三九已至,卻和暖如春嬉挡,著一層夾襖步出監(jiān)牢的瞬間叛氨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工棘伴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寞埠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓焊夸,卻偏偏與公主長(zhǎng)得像仁连,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子阱穗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354