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