最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java服務(wù)假死后續(xù)之內(nèi)存溢出的原因分析

 更新時(shí)間:2022年07月05日 10:25:02   作者:kbkb  
這篇文章主要介紹了Java服務(wù)假死后續(xù)之內(nèi)存溢出,本文給大家分享原因排查和故障解決方法,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

一、現(xiàn)象分析

上篇博客說到,Java服務(wù)假死的原因是使用了Guava緩存,30分鐘的有效期導(dǎo)致Full GC無法回收內(nèi)存。經(jīng)過優(yōu)化后,已經(jīng)不再使用Guava緩存,實(shí)時(shí)查詢數(shù)據(jù)。從短期效果來看,確實(shí)解決了無法回收內(nèi)存的問題,但是服務(wù)運(yùn)行幾天后,發(fā)現(xiàn)內(nèi)存又逐漸被占滿,F(xiàn)ull GC后只能回收一小部分。

從上圖可以看出,一次Full GC后,老年代基本上沒有回收多少內(nèi)存,占比從99.86%降到99.70%。

二、原因排查

到底是什么對象占據(jù)這么大的內(nèi)存,并且無法被JVM垃圾回收呢。在上一篇博客中已經(jīng)移除了Guava緩存,按理說不應(yīng)該有無法回收的對象了。那么,很明顯這應(yīng)該是代碼問題導(dǎo)致了內(nèi)存泄露,現(xiàn)在需要知道哪些對象無法被回收,從而定位出代碼哪里有BUG。這里采用jmap -histo:live 201349|head -10命令打印出GC后存活的對象。

從上圖可以看出,還是之前存在Guava緩存里面的對象占據(jù)著大部分內(nèi)存,代碼修改為實(shí)時(shí)查詢后,每次用完數(shù)據(jù)都會從Map中剔除,按理不應(yīng)該有強(qiáng)引用去引用這些對象。光看代碼無法排查出哪里導(dǎo)致了內(nèi)存泄露,只能將GC后的內(nèi)存文件導(dǎo)出來進(jìn)行分析。這里采用jmap -dump:format=b,file=/data/heap.hprof命令將內(nèi)存文件導(dǎo)出來,用JDK自帶的visualVM打開。

這里拿ECBug對象進(jìn)行分析,從引用關(guān)系可以看出,ECBug對象被DataSetCenter引用,DataSetCenter就是實(shí)時(shí)查詢數(shù)據(jù)進(jìn)行存儲的一個(gè)ConcurrentHashMap,但每次用完數(shù)據(jù)后都會進(jìn)行remove操作,具體代碼如下所示。

private List<BusinessBean> realTimeQueryBusinessModelData(IDataSetKey accessCacheDataSetKey,Set<IMapper> mappers, Set<IFilter> filters, Set<ISorter> sorters) throws DataNotFoundException, IllegalAccessException, CloneNotSupportedException, InstantiationException {
        List<BusinessBean> resultBeans = null;
        try {
            lock.lock();
            if (!dataSetCenter.containsKey(accessCacheDataSetKey)) {
                log.info("put DataSetKey into DataSetCenter,dataSetKey is {}",accessCacheDataSetKey);
                int count = businessModelQuery.count(accessCacheDataSetKey);
                if (count == 0) throw new DataNotFoundException();
                Class modelClass = businessModelCenter.getDataModelClass(accessCacheDataSetKey.getModelId());
                if (modelClass == null) {
                    throw new DataNotFoundException();
                }
                dataSetCenter.put(accessCacheDataSetKey, new DataSet(count, modelClass));
            }
            List<BusinessBean> cachedBeans = dataSetCenter.get(accessCacheDataSetKey).getData();
            resultBeans =  getModelDataInternal(accessCacheDataSetKey, businessModelQuery, mappers, filters, sorters, cachedBeans);
        }finally {
            lock.unlock();
            if(!lock.isLocked()){
                dataSetCenter.remove(accessCacheDataSetKey);
            }
        }
        return resultBeans;
    }

從代碼來看,每次dataSetCenter.put(accessCacheDataSetKey, new DataSet(count, modelClass))后,都會在finally里面調(diào)用dataSetCenter.remove(accessCacheDataSetKey)把key刪除掉,這樣在GC時(shí)會自動(dòng)回收Value值。但是忽略了一個(gè)方法getModelDataInternal,該方法可能會遞歸調(diào)用realTimeQueryBusinessModelData方法,如果存在遞歸調(diào)用的話,那么由于可重入鎖lock還沒有完成解鎖,所以無法進(jìn)入if(!lock.isLocked())條件語句中進(jìn)行刪除key的操作,這樣就造成了一部分?jǐn)?shù)據(jù)無法被刪除,隨著時(shí)間的推移,內(nèi)存中的數(shù)據(jù)會越來越多。

三、故障解決

基于上述的代碼分析,改造如下所示。

private List<BusinessBean> realTimeQueryBusinessModelData(IDataSetKey accessCacheDataSetKey,Set<IMapper> mappers, Set<IFilter> filters, Set<ISorter> sorters) throws DataNotFoundException, IllegalAccessException, CloneNotSupportedException, InstantiationException {
        List<BusinessBean> resultBeans = null;
        try {
            queryLock.lock();
            modelQueryLock.lock();
            if (!dataSetCenter.containsKey(accessCacheDataSetKey)) {
                log.info("put DataSetKey into DataSetCenter,dataSetKey is {}",accessCacheDataSetKey);
                int count = businessModelQuery.count(accessCacheDataSetKey);
                if (count == 0) throw new DataNotFoundException();
                Class modelClass = businessModelCenter.getDataModelClass(accessCacheDataSetKey.getModelId());
                if (modelClass == null) {
                    throw new DataNotFoundException();
                }
                dataSetCenter.put(accessCacheDataSetKey, new DataSet(count, modelClass));
            }
            List<BusinessBean> cachedBeans = dataSetCenter.get(accessCacheDataSetKey).getData();
            resultBeans =  getModelDataInternal(accessCacheDataSetKey, businessModelQuery, mappers, filters, sorters, cachedBeans);
        }finally {
            modelQueryLock.unlock();
            if(!modelQueryLock.isLocked()){
                removeDataSetKeys();
            }
            queryLock.unlock();
        }
        return resultBeans;
    }

這里當(dāng)modelQueryLock可重入鎖完全解鎖后,調(diào)用removeDataSetKeys方法,該方法會將dataSetCenter里面的key全部刪除,這樣在GC時(shí)就會回收不用的數(shù)據(jù)對象。這里采用兩個(gè)可重入鎖的目的是,如果只用一個(gè)modelQueryLock可重入鎖,那么當(dāng)modelQueryLock完全解鎖后,正在執(zhí)行removeDataSetKeys方法時(shí),其他線程就可以進(jìn)入該方法區(qū),發(fā)現(xiàn)dataSetCenter里面還沒有刪除完全,從而獲取里面的數(shù)據(jù),即if (!dataSetCenter.containsKey(accessCacheDataSetKey))為false,從而通過List<BusinessBean> cachedBeans = dataSetCenter.get(accessCacheDataSetKey).getData()直接獲取dataSetCenter里面的數(shù)據(jù),但是下一刻dataSetCenter里面可能已經(jīng)為空。因此,采用兩個(gè)可重入鎖,防止出現(xiàn)異常。

到此這篇關(guān)于Java服務(wù)假死后續(xù)之內(nèi)存溢出的文章就介紹到這了,更多相關(guān)Java&nbsp;內(nèi)存溢出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Kafka之kafka-topics.sh的使用解讀

    Kafka之kafka-topics.sh的使用解讀

    這篇文章主要介紹了Kafka之kafka-topics.sh的使用解讀,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 老生常談java中的fail-fast機(jī)制

    老生常談java中的fail-fast機(jī)制

    下面小編就為大家?guī)硪黄仙U刯ava中的fail-fast機(jī)制。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • java常見事件響應(yīng)方法實(shí)例匯總

    java常見事件響應(yīng)方法實(shí)例匯總

    這篇文章主要介紹了java常見事件響應(yīng)方法,對于初學(xué)者有很好的參考借鑒價(jià)值,分享給大家,需要的朋友可以參考下
    2014-08-08
  • 舉例講解JDK注解的使用和自定義注解的方法

    舉例講解JDK注解的使用和自定義注解的方法

    今天小編就為大家分享一篇關(guān)于舉例講解JDK注解的使用和自定義注解的方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • jar包加密方案分享

    jar包加密方案分享

    如何對jar包進(jìn)行加密呢?其實(shí)沒有想象中的那么困難,有一款開源工具已經(jīng)提供了較為完善的加密方案,這款開源工具的名字叫做xjar。接下來我們就看一下使用xjar工具給jar包加密有多么的容易。
    2021-06-06
  • 簡述springboot及springboot cloud環(huán)境搭建

    簡述springboot及springboot cloud環(huán)境搭建

    這篇文章主要介紹了簡述springboot及springboot cloud環(huán)境搭建的方法,包括spring boot 基礎(chǔ)應(yīng)用環(huán)境搭建,需要的朋友可以參考下
    2017-07-07
  • 詳解JDBC對Mysql utf8mb4字符集的處理

    詳解JDBC對Mysql utf8mb4字符集的處理

    這篇文章主要介紹了詳解JDBC對Mysql utf8mb4字符集的處理,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-11-11
  • Shiro + JWT + SpringBoot應(yīng)用示例代碼詳解

    Shiro + JWT + SpringBoot應(yīng)用示例代碼詳解

    這篇文章主要介紹了Shiro (Shiro + JWT + SpringBoot應(yīng)用),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-06-06
  • SpringBoot整合Redis將對象寫入redis的實(shí)現(xiàn)

    SpringBoot整合Redis將對象寫入redis的實(shí)現(xiàn)

    本文主要介紹了SpringBoot整合Redis將對象寫入redis的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Spring依賴注入的三種方式詳解

    Spring依賴注入的三種方式詳解

    這篇文章主要給大家介紹了三種Spring依賴注入的方式,?settter方法注入,構(gòu)造器注入以及變量(filed)?注入這三種方式,文章通過代碼示例給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-11-11

最新評論

永兴县| 阿瓦提县| 甘德县| 顺平县| 武清区| 遵义县| 常宁市| 繁昌县| 阿城市| 和龙市| 板桥市| 金山区| 汕头市| 青川县| 多伦县| 醴陵市| 金溪县| 米脂县| 工布江达县| 邻水| 手机| 安丘市| 汶上县| 高淳县| 汉阴县| 黄冈市| 青河县| 铁力市| 红桥区| 同德县| 游戏| 扎赉特旗| 双鸭山市| 西安市| 汉寿县| 林周县| 梁山县| 娄烦县| 玉树县| 襄汾县| 秦皇岛市|