自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

記一次隱藏很深的 JVM 線上慘案的分析、排查、解決

云計(jì)算 虛擬化
本文會(huì)給大家講解一個(gè)比較特殊的JVM優(yōu)化案例,這個(gè)優(yōu)化案例本身是因?yàn)樾率止こ處煂?duì)JVM優(yōu)化可能了解了一個(gè)半吊子,然后不知道從哪里找來了一個(gè)非常特殊的JVM參數(shù)錯(cuò)誤的設(shè)置了一下,就導(dǎo)致線上系統(tǒng)頻繁的出現(xiàn)Full GC的問題。

 [[276426]]

1、本文背景

本文會(huì)給大家講解一個(gè)比較特殊的JVM優(yōu)化案例,這個(gè)優(yōu)化案例本身是因?yàn)樾率止こ處煂?duì)JVM優(yōu)化可能了解了一個(gè)半吊子,然后不知道從哪里找來了一個(gè)非常特殊的JVM參數(shù)錯(cuò)誤的設(shè)置了一下,就導(dǎo)致線上系統(tǒng)頻繁的出現(xiàn)Full GC的問題。

但是我們后續(xù)大量的優(yōu)化案例其實(shí)都是各種各樣奇形怪狀的場景,因?yàn)檎歉鞣N奇怪場景才能讓大家逐步積累出來較為豐富的JVM優(yōu)化實(shí)戰(zhàn)經(jīng)驗(yàn)

了解的場景越多,自己未來在處理JVM性能問題的時(shí)候才能更是得心應(yīng)手。

2、問題的產(chǎn)生

這個(gè)場景的發(fā)生大致如下過程:某天團(tuán)隊(duì)里一個(gè)新手工程師大概是心血來潮,覺得自己網(wǎng)上看到了某個(gè)JVM參數(shù),以為學(xué)會(huì)了絕世武功秘籍,于是就在當(dāng)天上線一個(gè)系統(tǒng)的時(shí)候,自作主張?jiān)O(shè)置了一個(gè)JVM參數(shù)

這個(gè)參數(shù)是什么呢?

不用急,跟著看下面的案例分析即可,現(xiàn)在只要知道他設(shè)置了一個(gè)奇怪的參數(shù),接著事故就發(fā)生了。

因?yàn)橐话阒写笮凸径际墙尤腩愃芞abbix、OpenFalcon或者公司自研的一些監(jiān)控系統(tǒng)的,監(jiān)控系統(tǒng)一般都做的很好,可以讓你的系統(tǒng)直接接入進(jìn)去,然后在上面可以看到每臺(tái)機(jī)器的CPU、磁盤、內(nèi)存、網(wǎng)絡(luò)的一些負(fù)載。

而且可以看到你的JVM的內(nèi)存使用波動(dòng)折線圖,還有你的JVM GC發(fā)生的頻率折線圖。包括如果你自己上報(bào)某個(gè)業(yè)務(wù)指標(biāo),也可以在監(jiān)控系統(tǒng)里看到。

而且一般都會(huì)針對(duì)線上運(yùn)行的機(jī)器和系統(tǒng)設(shè)置一些報(bào)警,比如說,你可以設(shè)置如果10分鐘內(nèi)發(fā)現(xiàn)一個(gè)系統(tǒng)的JVM發(fā)生了超過3次Full GC,就必須發(fā)送報(bào)警給你,可以發(fā)送給你的短信、郵箱或者是釘釘之類的IM工具。

類似這樣的監(jiān)控系統(tǒng)不在我們的專欄范疇內(nèi),建議大家自己可以去查閱資料,其實(shí)基于我們講解的命令行工具,比如jstat,你可以通過linux上的一些命令,讓jstat自動(dòng)對(duì)jvm進(jìn)行監(jiān)控,把監(jiān)控結(jié)果可以輸出到機(jī)器的某個(gè)文件里去。

然后第二天你就可以去查閱那個(gè)文件,也可以看到那臺(tái)機(jī)器的jvm的一些gc統(tǒng)計(jì)。

所以說,沒有可視化工具,用最簡單的命令行工具,其實(shí)同樣可以起到類似的效果。

所以那天那個(gè)工程師設(shè)置了一個(gè)JVM參數(shù)之后,直接導(dǎo)致線上頻繁接到JVM的Full GC的報(bào)警,大家就很奇怪了,于是就開始排查那個(gè)系統(tǒng)了。

3、查看GC日志

之前已經(jīng)給大家講解過如何在啟動(dòng)系統(tǒng)的時(shí)候讓他輸出GC日志,所以一旦發(fā)現(xiàn)報(bào)警,直接登錄到線上機(jī)器,然后就看到對(duì)應(yīng)的GC日志了。

此時(shí)我們看到在GC日志中有大量的Full GC的記錄。

那么是為什么導(dǎo)致的Full GC呢?

在日志里,看到了一個(gè)“Metadata GC Threshold”的字樣,類似于如下日志:

【Full GC(Metadata GC Threshold)xxxxx, xxxxx】

從這里就知道,這頻繁的Full GC,實(shí)際上是JDK 1.8以后的Metadata元數(shù)據(jù)區(qū)導(dǎo)致的,也就是類似我們之前說的永久代。

這個(gè)Metadata區(qū)域一般是放一些加載到JVM里去的類的。

所以此時(shí)就很奇怪了,為什么會(huì)因?yàn)镸etadata區(qū)域頻繁的被塞滿,進(jìn)而觸發(fā)Full GC?而且Full GC大家都知道,會(huì)帶動(dòng)CMS回收老年代,還會(huì)回收Metadata區(qū)域本身。

我們先看看下圖:

記一次隱藏很深的 JVM 線上慘案的分析、排查、解決

4、查看Metaspace內(nèi)存占用情況

接著我們當(dāng)然是想看一看Metaspace區(qū)域的內(nèi)存占用情況了,簡單點(diǎn)你可以通過jstat來觀察,如果有監(jiān)控系統(tǒng),他會(huì)給你展示出來一個(gè)Metaspace內(nèi)存區(qū)域占用的波動(dòng)曲線圖,類似下面這種。

記一次隱藏很深的 JVM 線上慘案的分析、排查、解決

看起來Metaspace區(qū)域的內(nèi)存呈現(xiàn)一個(gè)波動(dòng)的狀態(tài),他總是會(huì)先不斷增加,達(dá)到一個(gè)頂點(diǎn)之后,就會(huì)把Metaspace區(qū)域給占滿,然后自然就會(huì)觸發(fā)一次Full GC,F(xiàn)ull GC會(huì)帶著Metaspace區(qū)域的垃圾回收,所以接下來Metaspace區(qū)域的內(nèi)存占用又變得很小了。

5、一個(gè)綜合性的分析思路

看到這里,相信大家肯定有一點(diǎn)感覺了,這個(gè)很明顯是系統(tǒng)在運(yùn)行過程中,不停的有新的類產(chǎn)生被加載到Metaspace區(qū)域里去,然后不停的把Metaspace區(qū)域占滿,接著觸發(fā)一次Full GC回收掉Metaspace區(qū)域中的部分類。

然后這個(gè)過程反復(fù)的不斷的循環(huán),進(jìn)而造成Metaspace區(qū)域反復(fù)被占滿,然后反復(fù)導(dǎo)致Full GC的發(fā)生,如下圖所示。

記一次隱藏很深的 JVM 線上慘案的分析、排查、解決

6、到底是什么類不停的被加載?

接著我們就有點(diǎn)奇怪了,到底是什么類不停的被加載到JVM的Metaspace區(qū)域里去?

這個(gè)時(shí)候就需要在JVM啟動(dòng)參數(shù)中加入如下兩個(gè)參數(shù)了:

  1. “-XX:TraceClassLoading -XX:TraceClassUnloading” 

這兩個(gè)參數(shù),顧名思義,就是追蹤類加載和類卸載的情況,他會(huì)通過日志打印出來JVM中加載了哪些類,卸載了哪些類。

加入這兩個(gè)參數(shù)之后,我們就可以看到在Tomcat的catalina.out日志文件中,輸出了一堆日志,里面顯示類似如下的內(nèi)容:

【Loaded sun.reflect.GeneratedSerializationConstructorAccessor from __JVM_Defined_Class】

明顯可以看到,JVM在運(yùn)行期間不停的加載了大量的所謂“GeneratedSerializationConstructorAccessor”類到了Metaspace區(qū)域里去

如下圖所示

記一次隱藏很深的 JVM 線上慘案的分析、排查、解決

相信就是因?yàn)镴VM運(yùn)行期間不停的加載這種奇怪的類,然后不停的把Metaspace區(qū)域占滿,才會(huì)引發(fā)不停的執(zhí)行Full GC的。

這是一個(gè)非常實(shí)用的技巧,各位同學(xué)一定要掌握,頻繁Full GC不光是老年代觸發(fā)的,有時(shí)候也會(huì)因?yàn)镸etaspace區(qū)域的類太多而觸發(fā)。

到此為止,已經(jīng)慢慢接近真相了。

7、為什么會(huì)頻繁加載奇怪的類?

接著遇到類似這種問題,我們就應(yīng)該找一下Google或者是百度了,當(dāng)然推薦是用Google。你完全可以看看那種不停加載的類,到底是什么類,是你自己寫的類?還是說JDK內(nèi)置的類?

比如上面的那個(gè)類,如果你查閱一些資料,很容易就會(huì)搞明白,那個(gè)類大概是在你使用Java中的反射時(shí)加載的,所謂反射代碼類似如下所示。

  1. Method method = XXX.class.getDeclaredMethod(xx,xx); 
  2. method.invoke(target,params); 

友情提示一下,反射是Java中最最基礎(chǔ)的一個(gè)概念,不懂的朋友自己查一下資料。

簡單來說,就是通過XXX.class獲取到某個(gè)類,然后通過geteDeclaredMethod獲取到那個(gè)類的方法。

這個(gè)方法就是一個(gè)Method對(duì)象,接著通過Method.invoke可以去調(diào)用那個(gè)類的某個(gè)對(duì)象的方法,大概就這個(gè)意思。

在執(zhí)行這種反射代碼時(shí),JVM會(huì)在你反射調(diào)用一定次數(shù)之后就動(dòng)態(tài)生成一些類,就是我們之前看到的那種莫名其妙的類

下次你再執(zhí)行反射的時(shí)候,就是直接調(diào)用這些類的方法,這是JVM的一個(gè)底層優(yōu)化的機(jī)制。

看到這里,有的小伙伴是不是有點(diǎn)蒙?

其實(shí)這倒無所謂,這段話看的蒙絲毫不影響你進(jìn)行JVM優(yōu)化的

你只要記住一個(gè)結(jié)論:如果你在代碼里大量用了類似上面的反射的東西,那么JVM就是會(huì)動(dòng)態(tài)的去生成一些類放入Metaspace區(qū)域里的。

所以上面看到的那些奇怪的類,就是由于不停的執(zhí)行反射的代碼才生成的,如下圖所示。

記一次隱藏很深的 JVM 線上慘案的分析、排查、解決

8、JVM創(chuàng)建的奇怪類有什么玄機(jī)?

那么接下來我們就很奇怪一件事情,就是JVM為什么要不停的創(chuàng)建那些奇怪的類然后放入Metaspace中去?

其實(shí)這就要從一個(gè)點(diǎn)入手來分析一下了,因?yàn)樯厦嬲f的那種JVM自己創(chuàng)建的奇怪的類,他們的Class對(duì)象都是SoftReference,也就是軟引用的。

大家可千萬別說連類的Class是什么都沒聽說過?簡單來說,每個(gè)類其實(shí)本身自己也是一個(gè)對(duì)象,就是一個(gè)Class對(duì)象,一個(gè)Class對(duì)象就代表了一個(gè)類。同時(shí)這個(gè)Class對(duì)象代表的類,可以派生出來很多實(shí)例對(duì)象。

舉例來說,Class Student,這就是一個(gè)類,他本身是由一個(gè)Class類型的對(duì)象表示的。

但是如果你走一個(gè)Student student = new Student(),這就是實(shí)例化了這個(gè)Student類的一個(gè)對(duì)象,這是一個(gè)Student類型的實(shí)例對(duì)象。

所以我們這里所說的Class對(duì)象,就是JVM在發(fā)射過程中動(dòng)態(tài)生成的類的Class對(duì)象,他們都是SoftReference軟引用的。

所謂的軟引用,最早我們?cè)僖黄恼吕镎f過,正常情況下不會(huì)回收,但是如果內(nèi)存比較緊張的時(shí)候就會(huì)回收這些對(duì)象。

那么SoftReference對(duì)象到底在GC的時(shí)候要不要回收是通過什么公式來判斷的呢?

是如下的一個(gè)公式:

clock - timestamp <= freespace * SoftRefLRUPolicyMSPerMB。

這個(gè)公式的意思就是說,“clock - timestamp”代表了一個(gè)軟引用對(duì)象他有多久沒被訪問過了,freespace代表JVM中的空閑內(nèi)存空間,SoftRefLRUPolicyMSPerMB代表每一MB空閑內(nèi)存空間可以允許SoftReference對(duì)象存活多久。

舉個(gè)例子,假如說現(xiàn)在JVM創(chuàng)建了一大堆的奇怪的類出來,這些類本身的Class對(duì)象都是被SoftReference軟引用的。

然后現(xiàn)在JVM里的空間內(nèi)存空間有3000MB,SoftRefLRUPolicyMSPerMB的默認(rèn)值是1000毫秒,那么就意味著,此時(shí)那些奇怪的SoftReference軟引用的Class對(duì)象,可以存活3000 * 1000 = 3000秒,就是50分鐘左右。

當(dāng)然上面都是舉例而已,大家都知道,一般來說發(fā)生GC時(shí),其實(shí)JVM內(nèi)部或多或少總有一些空間內(nèi)存的,所以基本上如果不是快要發(fā)生OOM內(nèi)存溢出了,一般軟引用也不會(huì)被回收。

所以大家就知道了,按理說JVM應(yīng)該會(huì)隨著反射代碼的執(zhí)行,動(dòng)態(tài)的創(chuàng)建一些奇怪的類,他們的Class對(duì)象都是軟引用的,正常情況下不會(huì)被回收,但是也不應(yīng)該快速增長才對(duì)。

9、為什么JVM創(chuàng)建的奇怪的類會(huì)不停的變多?

那么究竟為什么JVM創(chuàng)建的那些奇怪的類會(huì)不停的變多呢?

原因很簡單,因?yàn)槲恼麻_頭那個(gè)新手工程師不知道從哪里扒出來了SoftRefLRUPolicyMSPerMB這個(gè)JVM啟動(dòng)參數(shù),他直接把這個(gè)參數(shù)設(shè)置為0了。

他想的是,一旦這個(gè)參數(shù)設(shè)置為0,任何軟引用對(duì)象就可以盡快釋放掉,不用留存,盡量給內(nèi)存釋放空間出來,這樣不就可以提高內(nèi)存利用效率了么?

真是想的很傻很天真。

實(shí)際上一旦這個(gè)參數(shù)設(shè)置為0之后,直接導(dǎo)致clock - timestamp <= freespace * SoftRefLRUPolicyMSPerMB這個(gè)公式的右半邊是0,就導(dǎo)致所有的軟引用對(duì)象,比如JVM生成的那些奇怪的Class對(duì)象,剛創(chuàng)建出來就可能被一次Young GC給帶著立馬回收掉一些。

如下圖所示。

 

記一次隱藏很深的 JVM 線上慘案的分析、排查、解決

 

比如JVM好不容易給你弄出來100個(gè)奇怪的類,結(jié)果因?yàn)槟阆乖O(shè)置軟引用的參數(shù),導(dǎo)致突然一次GC就給你回收掉幾十個(gè)類

接著JVM在反射代碼執(zhí)行的過程中,就會(huì)繼續(xù)創(chuàng)建這種奇怪的類,在JVM的機(jī)制之下,會(huì)導(dǎo)致這種奇怪類越來越多。

也許下一次gc又會(huì)回收掉一些奇怪的類,但是馬上JVM還會(huì)繼續(xù)生成這種類,最終就會(huì)導(dǎo)致Metaspace區(qū)域被放滿了,一旦Metaspace區(qū)域被占滿了,就會(huì)觸發(fā)Full GC,然后回收掉很多類,接著再次重復(fù)上述循環(huán),如下圖所示。

記一次隱藏很深的 JVM 線上慘案的分析、排查、解決

其實(shí)很多人會(huì)有一個(gè)疑問,到底為什么軟引用的類因?yàn)殄e(cuò)誤的參數(shù)設(shè)置被快速回收之后,就會(huì)導(dǎo)致JVM不停創(chuàng)建更多的新的類呢?

其實(shí)大家不用去扣這里的細(xì)節(jié),這里有大量的底層JDK源碼的實(shí)現(xiàn),異常復(fù)雜,要真的說清楚,得好幾篇文章才能講清楚JDK底層源碼的這些細(xì)節(jié)。

大家只要記住這個(gè)結(jié)論,明白這個(gè)道理就好。

10、如何解決這個(gè)問題?

雖然底層JDK的一些實(shí)現(xiàn)細(xì)節(jié)我們沒分析,但是大致梳理出來了一個(gè)思路,大家也很清楚問題所在和原因了

解決方案很簡單。在有大量反射代碼的場景下,大家只要把

  1. -XX:SoftRefLRUPolicyMSPerMB=0 

這個(gè)參數(shù)設(shè)置大一些即可,千萬別讓一些新手同學(xué)設(shè)置為0,可以設(shè)置個(gè)1000,2000,3000,或者5000毫秒,都可以。

提高這個(gè)數(shù)值,就是讓反射過程中JVM自動(dòng)創(chuàng)建的軟引用的一些類的Class對(duì)象不要被隨便回收,當(dāng)時(shí)我們優(yōu)化這個(gè)參數(shù)之后,就可以看到系統(tǒng)穩(wěn)定運(yùn)行了。

基本上Metaspace區(qū)域的內(nèi)存占用是穩(wěn)定的,不會(huì)來回大幅度波動(dòng)了。

責(zé)任編輯:武曉燕 來源: 今日頭條
相關(guān)推薦

2023-01-04 18:32:31

線上服務(wù)代碼

2021-11-23 21:21:07

線上排查服務(wù)

2022-12-17 19:49:37

GCJVM故障

2021-05-31 10:08:44

工具腳本主機(jī)

2021-11-01 17:29:02

Windows系統(tǒng)Fork

2021-05-13 08:51:20

GC問題排查

2019-03-15 16:20:45

MySQL死鎖排查命令

2024-03-11 08:51:08

JVMSWAP內(nèi)存

2021-12-12 18:12:13

Hbase線上問題

2017-08-24 17:37:18

DNS緩存分析

2023-04-06 07:53:56

Redis連接問題K8s

2017-09-01 09:17:51

DNS緩存慘案

2022-02-08 17:17:27

內(nèi)存泄漏排查

2020-11-16 07:19:17

線上函數(shù)性能

2017-12-19 14:00:16

數(shù)據(jù)庫MySQL死鎖排查

2024-05-13 08:37:17

炫技H5UI

2022-11-16 08:00:00

雪花算法原理

2021-03-05 22:41:55

CDH集群CDH集群

2021-03-29 12:35:04

Kubernetes環(huán)境TCP

2024-10-10 15:32:51

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)