一文學(xué)會JVM性能優(yōu)化

實戰(zhàn)性能優(yōu)化

重新認(rèn)知JVM

之前我們畫過一張圖,是從Class文件到類裝載器,再到運行時數(shù)據(jù)區(qū)的過程细移,現(xiàn)在咱們把這張圖不妨豐富完善一下欢唾,展示了JVM的大體物理結(jié)構(gòu)圖且警。

執(zhí)行引擎:用于執(zhí)行JVM字節(jié)碼指令

主要由兩種實現(xiàn)方式:

(1)將輸入的字節(jié)碼指令在加載時或執(zhí)行時翻譯成另外一種虛擬機指令;

(2)將輸入的字節(jié)碼指令在加載時或執(zhí)行時翻譯成宿主主機本地CPU的指令集礁遣。這兩種方式對應(yīng)著字節(jié)碼的解釋執(zhí)行和即時編譯振湾。

9.2 堆內(nèi)存溢出

9.2.1 代碼

記得設(shè)置參數(shù)比如-Xmx20M -Xms20M

9.2.2 運行結(jié)果

訪問->http://localhost:8080/heap

Exception in thread "http-nio-8080-exec-2" java.lang.OutOfMemoryError: GC overhead limit exceeded

9.2.3 回顧jps和jinfo

9.2.4 回顧jmap手動導(dǎo)出和參數(shù)自動導(dǎo)出

jmap手動導(dǎo)出:jmap -dump:format=b,file=heap.hprof PID

參數(shù)自動導(dǎo)出:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump.hprof

9.3 方法區(qū)內(nèi)存溢出

比如向方法區(qū)中添加Class的信息

9.3.1 asm依賴和Class代碼


9.3.2 代碼

設(shè)置Metaspace的大小,比如-XX:MetaspaceSize=50M -XX:MaxMetaspaceSize=50M

9.3.3 運行結(jié)果

訪問->http://localhost:8080/nonheap

9.4 虛擬機棧

9.4.1 代碼演示StackOverFlow

9.4.2 運行結(jié)果

9.4.3 說明

Stack Space用來做方法的遞歸調(diào)用時壓入Stack Frame(棧幀)亡脸。所以當(dāng)遞歸調(diào)用太深的時候,就有可能耗盡Stack Space树酪,爆出StackOverflow的錯誤浅碾。

-Xss128k:設(shè)置每個線程的堆棧大小。JDK 5以后每個線程堆棧大小為1M续语,以前每個線程堆棧大小為256K垂谢。根據(jù)應(yīng)用的線程所需內(nèi)存大小進行調(diào)整。在相同物理內(nèi)存下疮茄,減小這個值能生成更多的線程滥朱。但是操作系統(tǒng)對一個進程內(nèi)的線程數(shù)還是有限制的,不能無限生成力试,經(jīng)驗值在3000~5000左右徙邻。

線程棧的大小是個雙刃劍,如果設(shè)置過小畸裳,可能會出現(xiàn)棧溢出缰犁,特別是在該線程內(nèi)有遞歸、大的循環(huán)時出現(xiàn)溢出的可能性更大怖糊,如果該值設(shè)置過大帅容,就有影響到創(chuàng)建棧的數(shù)量,如果是多線程的應(yīng)用伍伤,就會出現(xiàn)內(nèi)存溢出的錯誤并徘。

9.5 線程死鎖

9.5.1 代碼


?9.4.2 運行結(jié)果

9.4.3 jstack分析

把打印信息拉到最后可以發(fā)現(xiàn)

9.4.4 jvisualvm

將線程信息dump出來

9.6 垃圾收集

內(nèi)存被使用了之后,難免會有不夠用或者達(dá)到設(shè)定值的時候扰魂,就需要對內(nèi)存空間進行垃圾回收麦乞。

9.6.1 垃圾收集發(fā)生的時機

GC是由JVM自動完成的,根據(jù)JVM系統(tǒng)環(huán)境而定阅爽,所以時機是不確定的路幸。

當(dāng)然,我們可以手動進行垃圾回收付翁,比如調(diào)用System.gc()方法通知JVM進行一次垃圾回收简肴,但是具體什么時刻運行也無法控制。也就是說System.gc()只是通知要回收百侧,什么時候回收由JVM決定砰识。

但是不建議手動調(diào)用該方法能扒,因為消耗的資源比較大。

一般以下幾種情況會發(fā)生垃圾回收

(1)當(dāng)Eden區(qū)或者S區(qū)不夠用了

(2)老年代空間不夠用了

(3)方法區(qū)空間不夠用了

(4)System.gc()

雖然垃圾回收的時機是不確定的辫狼,但是可以結(jié)合之前一個對象的一輩子案例初斑,文字圖解再次梳理一下堆內(nèi)存回收的流程。

一個對象的一輩子

我是一個普通的Java對象,我出生在Eden區(qū),在Eden區(qū)我還看到和我長的很像的小兄弟,我們在Eden區(qū)中玩了挺長時間膨处。

有一天Eden區(qū)中的人實在是太多了,我就被迫去了Survivor區(qū)的“From”區(qū),自從去了Survivor區(qū),我就開始漂了,有時候在Survivor的“From”區(qū),有時候在Survivor的“To”區(qū),居無定所见秤。直到我18歲的時候,爸爸說我成人了,該去社會上闖闖了。

于是我就去了年老代那邊,年老代里,人很多,并且年齡都挺大的,我在這里也認(rèn)識了很多人真椿。在年老代里,我生活了20年(每次GC加一歲),然后被回收鹃答。

9.6.2 實驗環(huán)境準(zhǔn)備

我的本地機器使用的是jdk1.8和tomcat8.5,大家也可以使用linux上的tomcat突硝,然后把gc日志下載下來即可测摔。

9.6.3 GC日志文件

回顧升華一下垃圾收集器圖


要想分析日志的信息,得先拿到GC日志文件才行解恰,所以得先配置一下锋八,之前也看過這些參數(shù)。

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log

比如打開windows中的catalina.bat护盈,在第一行加上

set JAVA_OPTS=%JAVA_OPTS% -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:gc.log

這樣使用startup.bat啟動tomcat的時候就能夠在當(dāng)前目錄下拿到gc.log文件

可以看到默認(rèn)使用的是ParallelGC

9.6.3.1 Parallel GC日志

【吞吐量優(yōu)先】

2019-06-10T23:21:53.305+0800: 1.303: [GC (Allocation Failure) [PSYoungGen: 65536K[Young區(qū)回收前]->10748K[Young區(qū)回收后](76288K[Young區(qū)總大小])] 65536K[整個堆回收前]->15039K[整個堆回收后](251392K[整個堆總大小]), 0.0113277 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

`注意`如果回收的差值中間有出入挟纱,說明這部分空間是Old區(qū)釋放出來的

9.6.3.2 CMS日志

【停頓時間優(yōu)先】

參數(shù)設(shè)置

-XX:+UseConcMarkSweepGC

重啟tomcat獲取gc日志,這里的日志格式和上面差不多黄琼,不作分析樊销。

9.6.3.3 G1日志

G1日志格式參考鏈接:

https://blogs.oracle.com/poonam/understanding-g1-gc-logs

【停頓時間優(yōu)先】

why?

https://blogs.oracle.com/poonam/increased-heap-usage-with-g1-gc

參數(shù)設(shè)置

-XX:+UseG1GC

9.6.4 GC日志文件分析工具

9.6.4.1 gceasy

可以比較不同的垃圾收集器的吞吐量和停頓時間


9.6.4.2 GCViewer

9.6.5 G1調(diào)優(yōu)

是否選用G1垃圾收集器的判斷依據(jù)

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/G1.html#use_cases

(1)50%以上的堆被存活對象占用

(2)對象分配和晉升的速度變化非常大

(3)垃圾回收時間比較長

(1)使用G1GC垃圾收集器: -XX:+UseG1GC

修改配置參數(shù),獲取到gc日志脏款,使用GCViewer分析吞吐量和響應(yīng)時間

Throughput? ? ?MinPause? ? ?MaxPause? ? ? AvgPause? ?GCcount

? 99.16%? ? ? ? ? 0.00016s? ? ? ? 0.0137s? ? ? ? ?0.00559s? ? ? ? ?12

(2)調(diào)整內(nèi)存大小再獲取gc日志分析

-XX:MetaspaceSize=100M-Xms300M-Xmx300M

比如設(shè)置堆內(nèi)存的大小围苫,獲取到gc日志,使用GCViewer分析吞吐量和響應(yīng)時間

Throughput? ? ?MinPause? ? ?MaxPause? ? ? AvgPause? ?GCcount

98.89%? ? ? ? ? 0.00021s? ? ? ? 0.01531s? ? ? 0.00538s? ? ? ? ? 12

(3)調(diào)整最大停頓時間

-XX:MaxGCPauseMillis=200 設(shè)置最大GC停頓時間指標(biāo)

比如設(shè)置最大停頓時間撤师,獲取到gc日志剂府,使用GCViewer分析吞吐量和響應(yīng)時間

Throughput? ? ?MinPause? ? ?MaxPause? ? ? AvgPause? ?GCcount

?98.96%? ? ? ? ? ? 0.00015s? ? ? ? 0.01737s? ? ? 0.00574s? ? ? ? ? 12

(4)啟動并發(fā)GC時堆內(nèi)存占用百分比

-XX:InitiatingHeapOccupancyPercent=45 G1用它來觸發(fā)并發(fā)GC周期,基于整個堆的使用率,而不只是某一代內(nèi)存的使用比例。值為 0 則表示“一直執(zhí)行GC循環(huán))'. 默認(rèn)值為 45 (例如, 全部的 45% 或者使用了45%).

比如設(shè)置該百分比參數(shù)剃盾,獲取到gc日志腺占,使用GCViewer分析吞吐量和響應(yīng)時間

Throughput? ? ?MinPause? ? ?MaxPause? ? ? AvgPause? ?GCcount

? 98.11%? ? ? ? ? 0.00406s? ? ? ? 0.00532s? ? ? 0.00469s? ? ? ? ? 12

9.6.6 G1調(diào)優(yōu)的最佳實踐

官網(wǎng)建議:

(https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html#recommendations)

(1)不要手動設(shè)置新生代和老年代的大小,只要設(shè)置整個堆的大小

G1收集器在運行過程中痒谴,會自己調(diào)整新生代和老年代的大小

其實是通過adapt代的大小來調(diào)整對象晉升的速度和年齡衰伯,從而達(dá)到為收集器設(shè)置的暫停時間目標(biāo)

如果手動設(shè)置了大小就意味著放棄了G1的自動調(diào)優(yōu)

(2)不斷調(diào)優(yōu)暫停時間目標(biāo)

一般情況下這個值設(shè)置到100ms或者200ms都是可以的(不同情況下會不一樣),但如果設(shè)置成50ms就不太合理积蔚。暫停時間設(shè)置的太短意鲸,就會導(dǎo)致出現(xiàn)G1跟不上垃圾產(chǎn)生的速度。最終退化成Full GC。所以對這個參數(shù)的調(diào)優(yōu)是一個持續(xù)的過程怎顾,逐步調(diào)整到最佳狀態(tài)读慎。暫停時間只是一個目標(biāo),并不能總是得到滿足槐雾。

(3)使用-XX:ConcGCThreads=n來增加標(biāo)記線程的數(shù)量

IHOP如果閥值設(shè)置過高夭委,可能會遇到轉(zhuǎn)移失敗的風(fēng)險,比如對象進行轉(zhuǎn)移時空間不足募强。如果閥值設(shè)置過低株灸,就會使標(biāo)記周期運行過于頻繁,并且有可能混合收集期回收不到空間擎值。

> IHOP值如果設(shè)置合理蚂且,但是在并發(fā)周期時間過長時,可以嘗試增加并發(fā)線程數(shù)幅恋,調(diào)高ConcGCThreads。

(4)MixedGC調(diào)優(yōu)

-XX:InitiatingHeapOccupancyPercent

-XX:G1MixedGCLiveThresholdPercent

-XX:G1MixedGCCountTarger

-XX:G1OldCSetRegionThresholdPercent

(5)適當(dāng)增加堆內(nèi)存大小

9.7 一張圖總結(jié)JVM性能優(yōu)化


全文完泵肄!thanks for watching

歡迎關(guān)注“Java架構(gòu)師學(xué)習(xí)”

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捆交,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子腐巢,更是在濱河造成了極大的恐慌品追,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冯丙,死亡現(xiàn)場離奇詭異肉瓦,居然都是意外死亡,警方通過查閱死者的電腦和手機胃惜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門泞莉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人船殉,你說我怎么就攤上這事鲫趁。” “怎么了利虫?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵挨厚,是天一觀的道長。 經(jīng)常有香客問我糠惫,道長疫剃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任硼讽,我火速辦了婚禮巢价,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己蹄溉,他們只是感情好咨油,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著柒爵,像睡著了一般役电。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上棉胀,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天法瑟,我揣著相機與錄音,去河邊找鬼唁奢。 笑死霎挟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的麻掸。 我是一名探鬼主播酥夭,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼脊奋!你這毒婦竟也來了熬北?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤诚隙,失蹤者是張志新(化名)和其女友劉穎讶隐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體久又,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡巫延,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了地消。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炉峰。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖脉执,靈堂內(nèi)的尸體忽然破棺而出讲冠,到底是詐尸還是另有隱情,我是刑警寧澤适瓦,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布竿开,位于F島的核電站,受9級特大地震影響玻熙,放射性物質(zhì)發(fā)生泄漏否彩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一嗦随、第九天 我趴在偏房一處隱蔽的房頂上張望列荔。 院中可真熱鬧敬尺,春花似錦、人聲如沸贴浙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽崎溃。三九已至蜻直,卻和暖如春户誓,著一層夾襖步出監(jiān)牢的瞬間困曙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工掷豺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留囱修,地道東北人赎瑰。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像破镰,于是被迫代替她去往敵國和親餐曼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容