JVM原理探究及调优方法论
1 此文目的
本文不準(zhǔn)備從盤古開天地開始講述JVM的種種,相關(guān)的文章網(wǎng)上太多了,大多也無非轉(zhuǎn)來轉(zhuǎn)去,連圖都差不多。筆者只整理個提綱挈領(lǐng)的學(xué)習(xí)路線指南,并對自己學(xué)習(xí)過程中遇到的坑和容易混淆和忽視的地方作個總結(jié)。
2 JVM內(nèi)存模型
2.1 內(nèi)存模型
內(nèi)存區(qū)域劃分有多個維度,相同區(qū)域在不同維度的名稱并不一樣。如下圖所示
可以看到,survivor區(qū)被劃分為了survivor0和survivor1兩個區(qū)域,但是在講MinorGC的原理時,我們又會說survvior to和survivor from兩個區(qū)域。事實(shí)上,survivor0和survivor1是物理維度的劃分,而survivor to和survivor from是邏輯維度的劃分,在MinorGC的過程中,survivor0和survivor1交替擔(dān)當(dāng)to區(qū)和from區(qū)。 來仔細(xì)解釋一下MinorGC的過程: 在GC開始的時候,對象只會存在于Eden區(qū)和名為“From”的Survivor區(qū),Survivor區(qū)“To”是空的。緊接著進(jìn)行GC,Eden區(qū)中所有存活的對象都會被復(fù)制到“To”,而在“From”區(qū)中,仍存活的對象會根據(jù)他們的年齡值來決定去向。年齡達(dá)到一定值(年齡閾值,可以通過-XX:MaxTenuringThreshold來設(shè)置)的對象會被移動到年老代中,沒有達(dá)到閾值的對象會被復(fù)制到“To”區(qū)域。經(jīng)過這次GC后,Eden區(qū)和From區(qū)已經(jīng)被清空。這個時候,“From”和“To”會交換他們的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎樣,都會保證名為To的Survivor區(qū)域是空的。Minor GC會一直重復(fù)這樣的過程,直到“To”區(qū)被填滿,“To”區(qū)被填滿之后,會將所有對象移動到年老代中。大致如下圖所示:2.2 方法區(qū)和永久代
這兩個概念,很多時候都被當(dāng)做是同一個概念。實(shí)際上,“方法區(qū)”是java虛擬機(jī)規(guī)范中對存放類信息,字段,方法,常量,靜態(tài)變量,接口和常量池的內(nèi)存區(qū)域的定義,而“永久代”則是HotSpot VM在1.8版本以前對于方法區(qū)的具體實(shí)現(xiàn)。由于java虛擬機(jī)規(guī)范并沒有對方法區(qū)的具體實(shí)現(xiàn)作限制,所以HotSpot VM和JRocket VM對于方法區(qū)的實(shí)現(xiàn)都是不一樣的,JRocket中就沒有永久代的概念。而在1.8及1.8以后的版本中,HotSpot VM用"元空間"--metaspace來代替永久代,實(shí)現(xiàn)方法區(qū)。 這個變化帶來的就是VM參數(shù)的變化,所有的PermGen都被替換成了MetaSpace。并且metaSpace不再使用堆內(nèi)存,而是使用系統(tǒng)內(nèi)存。但是該發(fā)生的OOM一樣會發(fā)生。原因也基本都是加載到內(nèi)存中的 class 數(shù)量太多或者體積太大。
3.GC
3.1 GC算法
GC算法和GC收集器也是兩個維度的概念。 GC算法包括清除算法(也叫標(biāo)記清除算法),復(fù)制算法,標(biāo)記-整理算法。 不同垃圾收集器針對不同的內(nèi)存區(qū)域,采用不同的GC算法。 具體介紹,網(wǎng)上相關(guān)資料很多,可以參考這篇文章:blog.csdn.net/xiaoping091…
3.2 垃圾收集器
垃圾收集器經(jīng)歷了從串行收集器到并行收集器,再到并發(fā)收集器的進(jìn)化過程。這三者的區(qū)別如下圖所示
串行和并行的區(qū)別比較容易理解,而CMS垃圾收集器的原理要注意的是,雖然它是并發(fā)收集器,但它的GC線程并不是完完全全地與應(yīng)用的進(jìn)程并發(fā)進(jìn)行,它只是通過用兩次短暫停來代替并行GC的一次長暫停,以期達(dá)到減少應(yīng)用線程暫停的目的,詳見CMS垃圾回收機(jī)制不同版本默認(rèn)使用的垃圾收集器以及支持開發(fā)者定制的垃圾收集器都是不一樣的 jdk1.7 默認(rèn)垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代) jdk1.8 默認(rèn)垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代) jdk1.9 默認(rèn)垃圾收集器G1 與此同時,通過設(shè)置JVM參數(shù)也可以自己選擇垃圾收集器。如要開啟G1垃圾回收器,可以用-XX:+UseG1GC,支持G1垃圾回收器的JDK最低版本為JDK 7u4。在用戶自己選擇垃圾收集器的時候,要注意JDK版本的問題。 筆者用表格的形式列出了新生代和老年代的GC收集器的常見搭配方案:
3.3 Full GC觸發(fā)條件
頻繁FullGC導(dǎo)致的stop the world的現(xiàn)象,會大大影響系統(tǒng)的穩(wěn)定性。盡管一代又一代的垃圾收集器的優(yōu)化,使得stop the world的時間越來越短,但是在大型應(yīng)用中,還是避之不及。 出發(fā)FullGC的情況有以下幾種:
3.3.1 OOM的類型
通常情況下,JVM的GC機(jī)制能保證應(yīng)用的正常運(yùn)行,導(dǎo)致系統(tǒng)頻繁FullGC的原因百分之九十都是內(nèi)存溢出(OOM)。OOM分為以下幾類:
4. JVM調(diào)優(yōu)
4.1 調(diào)優(yōu)參數(shù)
正確設(shè)置JVM參數(shù),可以盡可能多地避免系統(tǒng)資源浪費(fèi),盡可能詳細(xì)地掌握系統(tǒng)運(yùn)行情況,并且對可能出現(xiàn)的問題防患于未然。
Xms:堆初始空間
Xmx:堆最大空間
Xmn:年輕代大小
XX:MaxNewSize 新生代最大空間 建議設(shè)置為整個堆的1/3到1/4
XX:NewSize
XX:MaxTenuringThreshold survivor中到老年代中的年齡閾值
Xss:每個線程的棧大小
java -XX:+PrintCommandLineFlags -version 得到JDK建議的內(nèi)存分配大小
tomcat設(shè)置catalina.sh:
export JAVA_OPTS="-server –Xms1024m -Xmx1024m -XX:+UseParallelOldGC -verbose:gc -Xloggc:../logs/gc.log
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps"
-XX:+PrintCommandLineFlagsjvm參數(shù)可查看默認(rèn)設(shè)置收集器類型
-XX:+PrintGCDetails亦可通過打印的GC日志的新生代、老年代名稱判斷
4.2 JVM監(jiān)控
-
1.本機(jī)環(huán)境下,推薦一款idea上的插件VisualVM Launcher,實(shí)際就是聯(lián)動了JDK開發(fā)包中自帶的jvisualvm.exe監(jiān)控軟件。也可以設(shè)置遠(yuǎn)程監(jiān)控。具體使用方法,可以參考這篇文章https://blog.csdn.net/wngpenghao/article/details/82884874IDEA Java性能分析插件VisualVM Launcher 配置(JAVA VisualVM 與Jconsole配置相同)
-
2.Linux的相關(guān)命令: jstat命令可以對jvm從各維度進(jìn)行統(tǒng)計,詳細(xì)使用參考jstat命令查看jvm的GC情況
-
3.VM參數(shù)設(shè)置時,指定打印出gc日志 -Xloggc:../logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 詳細(xì)的參數(shù)設(shè)置以及gc日志該如何閱讀,可以參考java之GC日志該怎么看
4.3 JVM異常排查
如果是使用jvisualvm就更方便了,直接點(diǎn)擊如圖所示的按鈕即可:
2. 分析dump eclipse有一款插件叫做Memory Analyzer(MAT),但是目前idea并沒有這款插件 此外,jhat是sun 1.6及以上版本中自帶的一個用于分析JVM 堆DUMP 文件的工具,基于此工具可分析JVM HEAP 中對象的內(nèi)存占用情況 jhat -J-Xmx1024M [file] 執(zhí)行后等待console 中輸入start HTTP server on port 7000 即可使用瀏覽器訪問 IP:7000 可以特別關(guān)心下圖標(biāo)出的這個選項(xiàng) 這對于排查堆內(nèi)存溢出非常有效4.4 實(shí)戰(zhàn)例子
由于實(shí)際工作中,能接觸到JVM機(jī)會的機(jī)會并不多,所以筆者整理了一些經(jīng)典實(shí)例
Metaspace溢出排查過程
分享一次 Java 內(nèi)存泄漏的排查
一次生產(chǎn)的 JVM 優(yōu)化案例
JVM成長之路,記錄一次內(nèi)存溢出導(dǎo)致頻繁FGC的問題排查及解決
非常詳細(xì)的jvm調(diào)優(yōu)實(shí)例,性能瓶頸定位
轉(zhuǎn)載于:https://juejin.im/post/5cf500c7f265da1b855c40c3
總結(jié)
以上是生活随笔為你收集整理的JVM原理探究及调优方法论的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java程序编译的几个方法(编辑器Not
- 下一篇: classpath环境变量