作為程式設計師必須掌握的Java虛擬機器中的22個重難點

java愛好者i發表於2019-04-01

Java虛擬機器一直是比較重要的知識點,是Java高階開發必會的。本文為你總結了關於JVM的22個重點、難點,圖文並茂的向你展示和JVM有關的重點知識。全文共7000字左右。

概念

虛擬機器:指以軟體的方式模擬具有完整硬體系統功能、執行在一個完全隔離環境中的完整計算機系統 ,是物理機的軟體實現。常用的虛擬機器有VMWare,Visual Box,Java Virtual Machine(Java虛擬機器,簡稱JVM)。

Java虛擬機器陣營:Sun HotSpot VM、BEA JRockit VM、IBM J9 VM、Azul VM、Apache Harmony、Google Dalvik VM、Microsoft JVM…

啟動流程

作為程式設計師必須掌握的Java虛擬機器中的22個重難點
基本架構

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

Java執行時編譯原始碼(.java)成位元組碼,由jre執行。jre由java虛擬機器(jvm)實現。Jvm分析位元組碼,後解釋並執行。

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

JVM由三個主要的子系統構成:

1.類載入器子系統

2.執行時資料區(記憶體)

3.執行引擎

類載入器子系統

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

類裝載包括了載入,連線(驗證、準備、解析(可選)),初始化。其中類載入工作由ClassLoader及其子類負責。

載入:在硬碟上查詢並通過IO讀入位元組碼檔案

連線:執行校驗、準備、解析(可選)步驟

校驗,校驗位元組碼檔案的正確性

準備,給類的靜態變數分配記憶體,並賦予預設值

解析,將符號引用轉為直接引用,類裝載器裝入類所引用的其他所有類

初始化:對類的靜態變數初始化為指定的值,執行靜態程式碼塊

類載入器體系結構

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

1.啟動類載入器:負責載入JRE的核心類庫,如jre目標下的rt.jar,charsets.jar等.

2.擴充套件類載入器:負責載入JRE擴充套件目錄ext中JAR類包

3.系統類載入器:負責載入ClassPath路徑下的類包

4.使用者自定義載入器:負責載入使用者自定義路徑下的類包

類載入機制(雙親委派)

全盤負責委託機制。全盤負責,當一個ClassLoader載入一個類時,除非顯示的使用另一個ClassLoader,該類所依賴和引用的類也由這個ClassLoader載入。委託機制:指先委託父類載入器尋找目標類,在找不到的情況下采用自己的路徑中查詢並載入目標類

執行時資料區

作為程式設計師必須掌握的Java虛擬機器中的22個重難點
堆(Java堆)

虛擬機器啟動時建立,用於存放物件例項,幾乎所有的物件(包含常量池)都在堆上分配記憶體,當物件無法再該空間申請到記憶體時將丟擲OutOfMemoryError異常。同時也是垃圾收集器管理的主要區域。可通過 -Xmx –Xms 引數來分別指定最大堆和最小堆。執行緒共享。

作為程式設計師必須掌握的Java虛擬機器中的22個重難點
棧(Java棧)

是java方法執行的記憶體模型,為虛擬機器執行java方法,每個方法在執行的同時都會建立一個棧幀(用於儲存區域性變數表,運算元棧,動態連結,方法出口等資訊)。執行緒獨佔。

作為程式設計師必須掌握的Java虛擬機器中的22個重難點
Jvm對該區域規範了兩種異常:

1,執行緒請求的棧深度大於虛擬機器棧所允許的深度,將丟擲StackOverFlowError異常。

2,若虛擬機器棧可動態擴充套件,當無法申請到足夠記憶體空間時將丟擲OutOfMemoryError。通過jvm引數–Xss指定棧空間,空間大小決定函式呼叫的深度。

本地方法棧

為虛擬機器執行native方法,其他規範與java棧類似。不同型別的虛擬機器對該區域可自由實現。執行緒獨佔。

PC暫存器(程式計數器)

用來儲存待執行指令的地址。分支,迴圈,跳轉,異常處理,執行緒恢復等功能都需要依賴pc暫存器。執行緒獨佔。

若執行緒執行的是一個java方法,則pc暫存器中儲存的是待執行指令的地址。若執行的是一個native方法,則pc暫存器中為空。

後設資料區

後設資料區取代了永久代,本質和永久代類似,都是對JVM規範中方法區的實現,區別在於後設資料區並不在虛擬機器中,而是使用本地記憶體。後設資料區在頻繁使用,也會發生OutOfMemory異常。

後設資料區的動態擴充套件,預設–XX:MetaspaceSize值為21MB的高水位線。一旦觸及則Full GC將被觸發並解除安裝沒有用的類(類對應的類載入器不再存活),然後高水位線將會重置。新的高水位線的值取決於GC後釋放的元空間。如果釋放的空間少,這個高水位線則上升。如果釋放空間過多,則高水位線下降。

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

執行引擎

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

執行引擎讀取執行時資料區的位元組碼並逐個執行

(1) 直譯器:直譯器更快地解釋位元組碼,但執行緩慢,解釋一句執行一句。

(2) JIT編譯器:JIT編譯器消除了直譯器的缺點。執行引擎通過直譯器轉換位元組碼,當它發現重複的程式碼時,將使用JIT編譯器,它編譯整個位元組碼並將其更改為原生程式碼。這個原生程式碼將直接用於重複的方法呼叫,這提高了系統的效能。

JIT的構成元件為:

中間程式碼生成器(Intermediate Code Generator):生成中間程式碼 。

程式碼優化器(Code Optimizer):負責優化上面生成的中間程式碼 。

目的碼生成器(Target Code Generator):負責生成機器程式碼或原生程式碼 。

分析器(Profiler):一個特殊元件,負責查詢熱點(被多次呼叫的方法)

(3) 垃圾收集器:收集和刪除未引用的物件。程式可呼叫System.gc()觸發垃圾收集,但不能保證執行。

本地方法介面(JNI):JNI將與本機方法庫進行互動,並提供執行引擎所需的本機庫。

本地方法庫:執行引擎所需的本機庫的集合。

垃圾收集(GC:Garbage Collection)

1,如何識別垃圾,判定物件是否可被回收?

引用計數法:給每個物件新增一個計數器,當有地方引用該物件時計數器加1,當引用失效時計數器減1。用物件計數器是否為0來判斷物件是否可被回收。缺點:無法解決迴圈引用的問題

根搜尋演算法:也稱可達性分析法,通過“GC ROOTs”的物件作為搜尋起始點,通過引用向下搜尋,所走過的路徑稱為引用鏈。通過物件是否有到達引用鏈的路徑來判斷物件是否可被回收(可作為GC ROOTs的物件:虛擬機器棧中引用的物件,方法區中類靜態屬性引用的物件,方法區中常量引用的物件,本地方法棧中JNI引用的物件)

2,Java 中的堆是 GC 收集垃圾的主要區域,GC 分為兩種:Minor GC、Full GC ( 或稱為 Major GC )。

Minor GC:新生代(Young Gen)空間不足時觸發收集,由於Java 中的大部分物件通常不需長久存活,新生代是GC收集頻繁區域,所以採用複製演算法。

Full GC:老年代(Old Gen )空間不足或元空間達到高水位線執行收集動作,由於存放大物件及長久存活下的物件,佔用記憶體空間大,回收效率低,所以採用標記-清除演算法。

GC演算法

按照回收策略劃分為:標記-清除演算法,標記-整理演算法,複製演算法。

1.標記-清除演算法:分為兩階段“標記”和“清除”。首先標記出哪些物件可被回收,在標記完成之後統一回收所有被標記的物件所佔用的記憶體空間。不足之處:1.無法處理迴圈引用的問題2.效率不高3.產生大量記憶體碎片(ps:空間碎片太多可能會導致以後在分配大物件的時候而無法申請到足夠的連續記憶體空間,導致提前觸發新一輪gc)

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

2.標記-整理演算法:分為兩階段“標記”和“整理”。首先標記出哪些物件可被回收,在標記完成後,將物件向一端移動,然後直接清理掉邊界以外的記憶體。

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

3.複製演算法:把記憶體空間劃為兩個相等的區域,每次只使用其中一個區域。gc時遍歷當前使用區域,把正在使用中的物件複製到另外一個區域中。演算法每次只處理正在使用中的物件,因此複製成本比較小,同時複製過去以後還能進行相應的記憶體整理,不會出現“碎片”問題。不足之處:1.記憶體利用率問題2.在物件存活率較高時,其效率會變低。

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

按分割槽對待可分為:增量收集演算法,分代收集演算法

1.增量收集:實時垃圾回收演算法,即:在應用進行的同時進行垃圾回收,理論上可以解決傳統分代方式帶來的問題。增量收集把對堆空間劃分成一系列記憶體塊,使用時先使用其中一部分,垃圾收集時把之前用掉的部分中的存活物件再放到後面沒有用的空間中,這樣可以實現一直邊使用邊收集的效果,避免了傳統分代方式整個使用完了再暫停的回收的情況。

2.分代收集:(商用預設)基於物件生命週期劃分為新生代、老年代、元空間,對不同生命週期的物件使用不同的演算法進行回收。

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

按系統執行緒可分為:序列收集演算法,並行收集演算法,併發收集演算法

1.序列收集:使用單執行緒處理垃圾回收工作,實現容易,效率較高。不足之處:1.無法發揮多處理器的優勢 2.需要暫停使用者執行緒

2.並行收集:使用多執行緒處理垃圾回收工作,速度快,效率高。理論上CPU數目越多,越能體現出並行收集器的優勢。不足之處:需要暫停使用者執行緒

3.併發收集:垃圾執行緒與使用者執行緒同時工作。系統在垃圾回收時不需要暫停使用者執行緒

GC收集器

垃圾收集演算法是記憶體回收的理論基礎,而垃圾收集器就是記憶體回收的具體實現。

1.Serial 收集器主要針對新生代的收集,是最基本最古老的收集器,它是單執行緒收集器,工作時必須暫停所有使用者執行緒。該收集器採用複製演算法。

Serial Old收集器主要針對老年代收集,採用標記-整理演算法,實現簡單高效,但會停頓。

作為程式設計師必須掌握的Java虛擬機器中的22個重難點
2.ParNew收集器是Serial的多執行緒版本,針對新生代採用複製演算法使用多執行緒進行垃圾收集(並行收集器,響應優先)。

3.Parallel Scavenge採用複製演算法針對新生代的多執行緒收集器(並行收集器,吞吐優先)。可控制吞吐量和停頓時間,即吞吐量 = 執行使用者程式碼時間 / (執行使用者程式碼時間+垃圾收集時間)。

Parallel Old收集器是Parallel Scavenge收集器的老年代版本(並行收集器),使用多執行緒和標記-整理演算法。

作為程式設計師必須掌握的Java虛擬機器中的22個重難點
4.CMS(Current MarkSweep)收集器針對老年代,是一種以獲取最短回收停頓時間為目標的收集器,它是一種併發收集器,採用的是標記-清除演算法。

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

5.G1的新生代類似於ParNew,採用複製演算法演算法,當新生代佔用達到一定比例的時候,開始收集。老年代類似於CMS,不同點是採用標記-整理演算法。

G1因此它是一款並行與併發收集器,能充分利用多CPU、多核環境。並且它能建立可預測的停頓時間模型。

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

與CMS收集器相比G1收集器有以下特點:

  1. 空間整合,G1收集器採用標記-整理演算法,不會產生記憶體空間碎片。分配大物件(直接進Humongous區,專門存放短期巨型物件,不用直接進老年代,避免Full GC的大量開銷)不會因為無法找到連續空間而提前觸發下一次GC。(年青代拷貝、老年代轉移物件無空閒分割槽、巨型物件無連續分割槽時觸發Full GC,開銷極大應該避免)

  2. 可預測停頓,降低停頓時間是G1和CMS的共同關注點,但G1除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明確指定在一個長度為N毫秒的時間內,消耗在垃圾收集上的時間不得超過N毫秒,幾乎達到Java實時系統(RTSJ)級的垃圾收集器。

3.G1將Java堆劃分為多個大小相等的獨立區域(Region),雖保留新生代和老年代的概念,但不再是物理隔閡了,它們都是(可以不連續)Region的集合。

收集器常用組合

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

作為程式設計師必須掌握的Java虛擬機器中的22個重難點
JVM效能調優思路

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

理解GC日誌

作為程式設計師必須掌握的Java虛擬機器中的22個重難點

[GC [PSYoungGen: 8192K->1000K(9216K)] 16004K->14604K(29696K), 0.0317424 secs] [Times: user=0.06 sys=0.00, real=0.03 secs][GC [PSYoungGen: 9192K->1016K(9216K)] 22796K->20780K(29696K), 0.0314567 secs] [Times: user=0.06 sys=0.00, real=0.03 secs][Full GC [PSYoungGen: 8192K->8192K(9216K)] [ParOldGen: 20435K->20435K(20480K)] 28627K->28627K(29696K), [Metaspace: 8469K->8469K(1056768K)], 0.1307495 secs] [Times: user=0.50 sys=0.00, real=0.13 secs][Full GC [PSYoungGen: 8192K->8192K(9216K)] [ParOldGen: 20437K->20437K(20480K)] 28629K->28629K(29696K), [Metaspace: 8469K->8469K(1056768K)], 0.1240311 secs] [Times: user=0.42 sys=0.00, real=0.12 secs]

常見異常

StackOverflowError:(棧溢位)OutOfMemoryError: Java heap space(堆空間不足)OutOfMemoryError: GC overhead limit exceeded (GC花費的時間超過 98%, 並且GC回收的記憶體少於 2%)

GC引數

堆疊設定

-Xss:每個執行緒的棧大小-Xms:初始堆大小,預設實體記憶體的1/64-Xmx:最大堆大小,預設實體記憶體的1/4-Xmn:新生代大小-XX:NewSize:設定新生代初始大小-XX:NewRatio:預設2表示新生代佔年老代的1/2,佔整個堆記憶體的1/3。-XX:SurvivorRatio:預設8表示一個survivor區佔用1/8的Eden記憶體,即1/10的新生代記憶體。-XX:MaxMetaspaceSize:設定元空間最大允許大小,預設不受限制,JVM Metaspace會進行動態擴充套件。

垃圾回收統計資訊

-XX:+PrintGC-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-Xloggc:filename

收集器設定

-XX:+UseSerialGC:設定序列收集器-XX:+UseParallelGC:設定並行收集器-XX:+UseParallelOldGC:老年代使用並行回收收集器-XX:+UseParNewGC:在新生代使用並行收集器-XX:+UseParalledlOldGC:設定並行老年代收集器-XX:+UseConcMarkSweepGC:設定CMS併發收集器-XX:+UseG1GC:設定G1收集器-XX:ParallelGCThreads:設定用於垃圾回收的執行緒數

並行收集器設定

-XX:ParallelGCThreads:設定並行收集器收集時使用的CPU數。並行收集執行緒數。-XX:MaxGCPauseMillis:設定並行收集最大暫停時間-XX:GCTimeRatio:設定垃圾回收時間佔程式執行時間的百分比。公式為1/(1+n)CMS收集器設定-XX:+UseConcMarkSweepGC:設定CMS併發收集器-XX:+CMSIncrementalMode:設定為增量模式。適用於單CPU情況。-XX:ParallelGCThreads:設定併發收集器新生代收集方式為並行收集時,使用的CPU數。並行收集執行緒數。-XX:CMSFullGCsBeforeCompaction:設定進行多少次CMS垃圾回收後,進行一次記憶體壓縮-XX:+CMSClassUnloadingEnabled:允許對類後設資料進行回收-XX:UseCMSInitiatingOccupancyOnly:表示只在到達閥值的時候,才進行CMS回收-XX:+CMSIncrementalMode:設定為增量模式。適用於單CPU情況-XX:ParallelCMSThreads:設定CMS的執行緒數量-XX:CMSInitiatingOccupancyFraction:設定CMS收集器在老年代空間被使用多少後觸發-XX:+UseCMSCompactAtFullCollection:設定CMS收集器在完成垃圾收集後是否要進行一次記憶體碎片的整理

G1收集器設定

-XX:+UseG1GC:使用G1收集器-XX:ParallelGCThreads:指定GC工作的執行緒數量-XX:G1HeapRegionSize:指定分割槽大小(1MB~32MB,且必須是2的冪),預設將整堆劃分為2048個分割槽-XX:GCTimeRatio:吞吐量大小,0-100的整數(預設9),值為n則系統將花費不超過1/(1+n)的時間用於垃圾收集-XX:MaxGCPauseMillis:目標暫停時間(預設200ms)-XX:G1NewSizePercent:新生代記憶體初始空間(預設整堆5%)-XX:G1MaxNewSizePercent:新生代記憶體最大空間-XX:TargetSurvivorRatio:Survivor填充容量(預設50%)-XX:MaxTenuringThreshold:最大任期閾值(預設15)-XX:InitiatingHeapOccupancyPercen:老年代佔用空間超過整堆比IHOP閾值(預設45%),超過則執行混合收集-XX:G1HeapWastePercent:堆廢物百分比(預設5%)-XX:G1MixedGCCountTarget:引數混合週期的最大總次數(預設8)

效能分析和監控工具

Jps:虛擬機器程式狀況工具

Jstat:虛擬機器統計資訊監視工具

Jinfo:虛擬機器配置資訊工具

Jmap:記憶體映像工具

Jhat:虛擬機器堆轉儲快照分析工具

Jstack:堆疊跟蹤工具

JConsole:java監視與管理控制檯

VisualVM:故障處理工具

END

歡迎工作一到五年的Java工程師朋友們加入Java架構師:697558955

群內提供免費的Java架構學習資料(裡面有高可用、高併發、高效能及分散式、Jvm效能調優、Spring原始碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!

相關文章