一篇文章完全搞懂Java虛擬機

概念:java

虛擬機:指以軟件的方式模擬具備完整硬件系統功能、運行在一個徹底隔離環境中的完整計算機系統 ,是物理機的軟件實現。經常使用的虛擬機有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運行時編譯源碼(.java)成字節碼,由jre運行。jre由java虛擬機(jvm)實現。Jvm分析字節碼,後解釋並執行。jvm

 

 

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

        1.類加載器子系統工具

        2.運行時數據區(內存)性能

        3.執行引擎優化

 

類加載器子系統


 

類裝載包括了加載,鏈接(驗證、準備、解析(可選)),初始化。其中類加載工做由ClassLoader及其子類負責。

        加載:在硬盤上查找並經過IO讀入字節碼文件

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

        校驗,校驗字節碼文件的正確性

        準備,給類的靜態變量分配內存,並賦予默認值

        解析,將符號引用轉爲直接引用,類裝載器裝入類所引用的其餘全部類

 

 

        初始化:對類的靜態變量初始化爲指定的值,執行靜態代碼塊      

 

類加載器體系結構

 

 

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

2.擴展類加載器:負責加載JRE擴展目錄ext中JAR類包

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

4.用戶自定義加載器:負責加載用戶自定義路徑下的類包

 

類加載機制(雙親委派)

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

 

運行時數據區

 

 

堆(Java堆)

虛擬機啓動時建立,用於存放對象實例,幾乎全部的對象(包含常量池)都在堆上分配內存,當對象沒法再該空間申請到內存時將拋出OutOfMemoryError異常。同時也是垃圾收集器管理的主要區域。可經過 -Xmx –Xms 參數來分別指定最大堆和最小堆。線程共享。

 

 

棧(Java棧)

是java方法執行的內存模型,爲虛擬機執行java方法,每一個方法在執行的同時都會建立一個棧幀(用於存儲局部變量表,操做數棧,動態連接,方法出口等信息)。線程獨佔。

 

 

Jvm對該區域規範了兩種異常:

        1,線程請求的棧深度大於虛擬機棧所容許的深度,將拋出StackOverFlowError異常。

        2,若虛擬機棧可動態擴展,當沒法申請到足夠內存空間時將拋出OutOfMemoryError。經過jvm參數–Xss指定棧空間,空間大小決定函數調用的深度。

本地方法棧

爲虛擬機執行native方法,其餘規範與java棧相似。不一樣類型的虛擬機對該區域可自由實現。線程獨佔。

PC寄存器(程序計數器)

用來存儲待執行指令的地址。分支,循環,跳轉,異常處理,線程恢復等功能都須要依賴pc寄存器。線程獨佔。

若線程執行的是一個java方法,則pc寄存器中保存的是待執行指令的地址。若執行的是一個native方法,則pc寄存器中爲空。

 

元數據區

元數據區取代了永久代,本質和永久代相似,都是對JVM規範中方法區的實現,區別在於元數據區並不在虛擬機中,而是使用本地內存。元數據區在頻繁使用,也會發生OutOfMemory異常。

元數據區的動態擴展,默認–XX:MetaspaceSize值爲21MB的高水位線。一旦觸及則Full GC將被觸發並卸載沒有用的類(類對應的類加載器再也不存活),而後高水位線將會重置。新的高水位線的值取決於GC後釋放的元空間。若是釋放的空間少,這個高水位線則上升。若是釋放空間過多,則高水位線降低。


 

執行引擎

 

 

執行引擎讀取運行時數據區的字節碼並逐個執行

        (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)

 

 

        2.標記-整理算法:分爲兩階段「標記」和「整理」。首先標記出哪些對象可被回收,在標記完成後,將對象向一端移動,而後直接清理掉邊界之外的內存。

 

 

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

 

 

按分區對待可分爲:增量收集算法,分代收集算法

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

        2.分代收集:(商用默認)基於對象生命週期劃分爲新生代、老年代、元空間,對不一樣生命週期的對象使用不一樣的算法進行回收。

 

 

按系統線程可分爲:串行收集算法,並行收集算法,併發收集算法

        1.串行收集:使用單線程處理垃圾回收工做,實現容易,效率較高。不足之處:1.沒法發揮多處理器的優點 2.須要暫停用戶線程

        2.並行收集:使用多線程處理垃圾回收工做,速度快,效率高。理論上CPU數目越多,越能體現出並行收集器的優點。不足之處:須要暫停用戶線程

        3.併發收集:垃圾線程與用戶線程同時工做。系統在垃圾回收時不須要暫停用戶線程

 

GC收集器

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

        1.Serial 收集器主要針對新生代的收集,是最基本最古老的收集器,它是單線程收集器,工做時必須暫停全部用戶線程。該收集器採用複製算法。

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

        2.ParNew收集器是Serial的多線程版本,針對新生代採用複製算法使用多線程進行垃圾收集(並行收集器,響應優先)。

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

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

        4.CMS(Current MarkSweep)收集器針對老年代,是一種以獲取最短回收停頓時間爲目標的收集器,它是一種併發收集器,採用的是標記-清除算法。

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

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


 

與CMS收集器相比G1收集器有如下特色:

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

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

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

 

收集器經常使用組合

 

JVM性能調優思路

 

理解GC日誌

 

[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: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收集器在完成垃圾收集後是否要進行一次內存碎片的整理

 

垃圾回收統計信息

-XX:+PrintGC

-XX:+PrintGCDetails

-XX:+PrintGCTimeStamps

-Xloggc:filename

 

 

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)

 

收集器設置

-XX:+UseSerialGC:設置串行收集器

-XX:+UseParallelGC:設置並行收集器

-XX:+UseParallelOldGC:老年代使用並行回收收集器

-XX:+UseParNewGC:在新生代使用並行收集器

-XX:+UseParalledlOldGC:設置並行老年代收集器

-XX:+UseConcMarkSweepGC:設置CMS併發收集器

-XX:+UseG1GC:設置G1收集器

-XX:ParallelGCThreads:設置用於垃圾回收的線程數

 

性能分析和監控工具

Jps:虛擬機進程情況工具

Jstat:虛擬機統計信息監視工具

Jinfo:虛擬機配置信息工具

Jmap:內存映像工具

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

Jstack:堆棧跟蹤工具

JConsole:java監視與管理控制檯

VisualVM:故障處理工具

 

                                                ·END·

相關文章
相關標籤/搜索