java內(nèi)存異常使用導(dǎo)致full?gc頻繁
問(wèn)題系統(tǒng)
日常巡檢發(fā)現(xiàn),應(yīng)用線上出現(xiàn)頻繁full gc
現(xiàn)象
應(yīng)用線上出現(xiàn)頻繁full gc

排查過(guò)程
分析dump
拉dump文件:小插曲:dump時(shí)如果指定:live,則在dump前jvm會(huì)先進(jìn)行一次full gc,并且gc log里會(huì)打印dump full gc,這種對(duì)非內(nèi)存泄漏導(dǎo)致的線上異常內(nèi)存情況排查反而會(huì)帶來(lái)不便,導(dǎo)致我們多dump了好幾次。
分析dump文件:
a. 發(fā)現(xiàn)大量long[]數(shù)組占用最大空間,有異常情況

b. 查看gc根節(jié)點(diǎn),發(fā)現(xiàn)這些long[]數(shù)據(jù)大部分是被org.HdrHistogram.Histogram持有,每個(gè)Histogram對(duì)象會(huì)持有一個(gè)2048size的long[]
c. 查看Histogram實(shí)例的數(shù)量,竟然有5w個(gè),對(duì)比下正常項(xiàng)目的堆棧,大約是100倍

d. 這里又有一個(gè)插曲,一開(kāi)始習(xí)慣用mat分析,但是mat生成的報(bào)告對(duì)分析泄露比較有用,對(duì)于分析異常的內(nèi)存沒(méi)有jvisualvm.exe和idea的profiler好用
排查原因
本地啟動(dòng),可以復(fù)現(xiàn)這個(gè)類的內(nèi)存使用情況,于是本地起一個(gè)其他內(nèi)存正常的服務(wù)與有問(wèn)題的應(yīng)用,分析內(nèi)存對(duì)比
這里用的是idea的profiler,很方便
發(fā)現(xiàn)差異:
對(duì)比正常的應(yīng)用,發(fā)現(xiàn)異常應(yīng)用的引用存在異常的來(lái)自

● rx.internal.operators.OnSubscribeReduceSeed$ReduceSeedSubscriber的引用,懷疑就是這個(gè)異常引用就是導(dǎo)致這些實(shí)例無(wú)法在新生代回收而是堆積到了老年代觸發(fā)full gc的原因
排查差異:
簡(jiǎn)單看了下相關(guān)代碼,看不出個(gè)所以然,直接debug對(duì)比
系統(tǒng)確實(shí)走進(jìn)了相關(guān)的代碼,增加了對(duì)Histogram的引用,而正常應(yīng)用沒(méi)有
但是光這樣也看不出來(lái)為什么,此時(shí)關(guān)注到了左下角的線程池,這個(gè)線程池比較奇怪,是Metric的線程池
Metric是Hystrix用來(lái)統(tǒng)計(jì)相關(guān)指標(biāo),來(lái)供自己的dashboard或者用戶來(lái)獲取,以此來(lái)了解系統(tǒng)熔斷相關(guān)參數(shù)和指標(biāo)的功能
再看堆棧,走到這里的邏輯是

這個(gè)流用來(lái)統(tǒng)計(jì)單位時(shí)間內(nèi)的系統(tǒng)指標(biāo),導(dǎo)致Hystrix使用Histogram的long數(shù)組實(shí)現(xiàn)類似滑動(dòng)窗口的效果統(tǒng)計(jì)單位時(shí)間內(nèi)的指標(biāo)
Histogram本身是Hystrix用來(lái)實(shí)現(xiàn)類似桶+滑動(dòng)窗口的功能,來(lái)統(tǒng)計(jì)單位時(shí)間內(nèi)的流量,但是因?yàn)殚_(kāi)啟了指標(biāo)參數(shù),導(dǎo)致hystrix為了統(tǒng)計(jì)更長(zhǎng)時(shí)間范圍內(nèi)的指標(biāo),新增了對(duì)象持有更多(單位時(shí)間內(nèi))的Histogram引用來(lái)聚合,這部分引用因?yàn)槭墙y(tǒng)計(jì)更長(zhǎng)時(shí)間范圍周期的,就會(huì)因?yàn)橐贸钟袝r(shí)間長(zhǎng)而到老年代,但是本質(zhì)并不是內(nèi)存泄漏,所以每次full gc后又可以得到回收
解決問(wèn)題
看到上面的差異和怪異的線程池,第一反應(yīng)就是關(guān)閉metric使應(yīng)用不走到這段邏輯中增加引用,看官方文檔,該配置默認(rèn)是打開(kāi)的,并且確認(rèn)該功能只影響指標(biāo)統(tǒng)計(jì)不影響斷路器本身功能,使用配置hystrix.metrics.enabled=false配置來(lái)關(guān)閉
新增配置后,驗(yàn)證并查看堆棧,引用恢復(fù)正常,并且系統(tǒng)在一段時(shí)間后并沒(méi)有新增更多的Histogram實(shí)例,發(fā)布線上后觀察一段時(shí)間,full gc問(wèn)題確實(shí)得到解決
根本原因
在當(dāng)時(shí)發(fā)現(xiàn)解決的辦法并驗(yàn)證后,并沒(méi)有時(shí)間去研究hystrix.metrics.enabled默認(rèn)配置就是true但是其他應(yīng)用沒(méi)有出現(xiàn)這個(gè)full gc問(wèn)題的原因, 先解決了之后后續(xù)再繼續(xù)跟進(jìn)排查根本原因防止其他項(xiàng)目也出現(xiàn)相同問(wèn)題
之前發(fā)現(xiàn)可疑的線程池是HystrixMetricsPoller ,經(jīng)過(guò)查看,該線程池由HystrixMetricsPollerConfiguration

類開(kāi)啟,主要依靠hystrix.metrics.enabled開(kāi)啟,但是默認(rèn)是true,為什么其他項(xiàng)目沒(méi)有開(kāi)啟呢?
搜了下源碼,這個(gè)類的開(kāi)啟還和一個(gè)注解有關(guān)

對(duì)比了一下代碼,果然只有異常的應(yīng)用使用了這個(gè)注解,這個(gè)注解的目的是開(kāi)啟斷路器
但是研究之后發(fā)現(xiàn),不使用這個(gè)注解,熔斷等功能依舊可用,原因是在spring-cloud高版本之后,spring通過(guò)使用hystrix封裝openfeign的方法來(lái)使用熔斷,而不是集成整個(gè)hystrix體系,可能spring-cloud也發(fā)現(xiàn)了hystrix內(nèi)存使用上的問(wèn)題
所以在較高版本(起碼我們的版本),feign是通過(guò)feign.hystrix.enabled來(lái)開(kāi)關(guān)斷路器的(這個(gè)開(kāi)關(guān)是關(guān)閉的話,單純加@EnableCircuitBreaker注解斷路器是不會(huì)生效的)
其實(shí)在更高點(diǎn)版本的spring-cloud中,@EnableCircuitBreaker這個(gè)注解已經(jīng)被標(biāo)注為廢棄了,但是可能因?yàn)槲覀兪侵虚g版本,所以存在既沒(méi)有標(biāo)注廢棄其實(shí)又沒(méi)有什么用的情況
總而言之,feign的斷路功能只通過(guò)feign.hystrix.enabled來(lái)控制,增加@EnableCircuitBreaker注解之后僅僅只是會(huì)開(kāi)啟Hystrix其他所有的指標(biāo)等功能

問(wèn)題總結(jié)
問(wèn)題根本原因
本次問(wèn)題產(chǎn)生的根本原因是因?yàn)殚_(kāi)啟了@EnableCircuitBreaker注解,開(kāi)啟了Hystrix指標(biāo)功能,導(dǎo)致Histogram實(shí)例大量進(jìn)入老年代,只有full gc才可以回收
Histogram本身是Hystrix用來(lái)實(shí)現(xiàn)類似桶+滑動(dòng)窗口的功能,來(lái)統(tǒng)計(jì)單位時(shí)間內(nèi)的流量,但是因?yàn)殚_(kāi)啟了指標(biāo)參數(shù),導(dǎo)致hystrix為了統(tǒng)計(jì)更長(zhǎng)時(shí)間范圍內(nèi)的指標(biāo),新增了對(duì)象持有更多(單位時(shí)間內(nèi))的Histogram引用來(lái)聚合,這部分引用因?yàn)槭墙y(tǒng)計(jì)更長(zhǎng)時(shí)間范圍周期的,在訪問(wèn)量上升新生代復(fù)制速度變快時(shí),就會(huì)因?yàn)橐贸钟袝r(shí)間長(zhǎng)而到老年代,但是本質(zhì)并不是內(nèi)存泄漏,所以每次full gc后又可以得到回收
后續(xù)關(guān)注點(diǎn) Spring-Cloud本身體系比較復(fù)雜,因?yàn)楹蚇etfilx套件糾纏不清加上很多歷史原因,能用明白某一個(gè)版本的就很不錯(cuò)了 開(kāi)發(fā)本身并不了解這個(gè)版本斷路器到底怎么開(kāi)啟,沒(méi)有仔細(xì)看過(guò)對(duì)應(yīng)版本的官方文檔就去使用注解,在老版本,斷路器確實(shí)是通過(guò)這個(gè)注解才能啟用的 解決方式
關(guān)閉metric功能或者去掉@EnableCircuitBreaker注解均可解決
百度spring-cloud教程和文檔時(shí),一定一定一定要看對(duì)應(yīng)版本的,否則可能加一堆配置解決某個(gè)問(wèn)題,結(jié)果開(kāi)啟一大堆亂七八糟的功能
到此這篇關(guān)于java內(nèi)存異常使用導(dǎo)致full gc頻繁的文章就介紹到這了,更多相關(guān)java內(nèi)存異常導(dǎo)致full gc頻繁內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot集成Swagger2實(shí)現(xiàn)Restful(類型轉(zhuǎn)換錯(cuò)誤解決辦法)
這篇文章主要介紹了SpringBoot集成Swagger2實(shí)現(xiàn)Restful(類型轉(zhuǎn)換錯(cuò)誤解決辦法),需要的朋友可以參考下2017-07-07
Spring Security OAuth2 授權(quán)碼模式的實(shí)現(xiàn)
這篇文章主要介紹了Spring Security OAuth2 授權(quán)碼模式的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
Servlet3.0學(xué)習(xí)總結(jié)之基于Servlet3.0的文件上傳實(shí)例
本篇文章主要介紹了Servlet3.0學(xué)習(xí)總結(jié)之基于Servlet3.0的文件上傳實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07
使用Java快速將Web中表格轉(zhuǎn)換成Excel的方法
在平時(shí)做系統(tǒng)項(xiàng)目時(shí),經(jīng)常會(huì)需要做導(dǎo)出功能,下面這篇文章主要給大家介紹了關(guān)于使用Java快速將Web中表格轉(zhuǎn)換成Excel的相關(guān)資料,需要的朋友可以參考下2023-06-06
學(xué)習(xí)java編程后可以走哪些職業(yè)道路
在本篇文章里給大家介紹了關(guān)于學(xué)習(xí)java后的職業(yè)道路,以及需要學(xué)習(xí)的相關(guān)知識(shí)內(nèi)容,有興趣的朋友們可以跟著學(xué)習(xí)下。2022-11-11
使用java + selenium + OpenCV破解騰訊防水墻滑動(dòng)驗(yàn)證碼功能
這篇文章主要介紹了使用java + selenium + OpenCV破解騰訊防水墻滑動(dòng)驗(yàn)證碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11
Spring?Boot教程之提高開(kāi)發(fā)效率必備工具lombok
這篇文章主要介紹了Spring?Boot教程之提高開(kāi)發(fā)效率必備工具lombok的相關(guān)資料,需要的朋友可以參考下2022-08-08
Java java.lang.ExceptionInInitializerError 錯(cuò)誤如何解決
這篇文章主要介紹了 Java java.lang.ExceptionInInitializerError 錯(cuò)誤如何解決的相關(guān)資料,需要的朋友可以參考下2017-06-06

