JVM知識點總結
JVM總體梳理
一、jvm體系總體概覽
JVM體系總體分四大塊:類的加載機制、jvm內存結構、GC算法 垃圾回收、GC分析 命令調優
這裏畫了一個思維導圖,將所有的知識點進行了陳列,因為圖比較大可以點擊右鍵下載了放大查看。
二、類的加載機制
主要關註點:什麽是類的加載、類的生命周期、類加載器、雙親委派模型
什麽是類的加載
類的加載指的是將類的.class文件中的二進制數據讀入到內存中,將其放在運行時數據區的方法區內,然後在堆區創建一個java.lang.Class對象,用來封裝類在方法區內的數據結構。類的加載的最終產品是位於堆區中的Class對象,Class對象封裝了類在方法區內的數據結構,並且向Java程序員提供了訪問方法區內的數據結構的接口。
類的生命周期
類的生命周期包括這幾個部分,加載、連接、初始化、使用和卸載,其中前三部是類的加載的過程,如下圖:
加載,查找並加載類的二進制數據,在Java堆中也創建一個java.lang.Class類的對象
連接,連接又包含三塊內容:驗證、準備、解析。1)驗證,文件格式、元數據、字節碼、符號引用驗證;2)準備,為類的靜態變量分配內存,並將其初始化為默認值;3)解析,把類中的符號引用轉換為直接引用
初始化,為類的靜態變量賦予正確的初始值
使用,new出對象程序中使用
卸載,執行垃圾回收
幾個小問題?
1、JVM初始化步驟 ? 2、類初始化時機 ?3、哪幾種情況下,Java虛擬機將結束生命周期?
答案參考這篇文章jvm系列(一):java類的加載機制
類加載器
啟動類加載器:Bootstrap ClassLoader,負責加載存放在JDK\jre\lib(JDK代表JDK的安裝目錄,下同)下,或被-Xbootclasspath參數指定的路徑中的,並且能被虛擬機識別的類庫
擴展類加載器:Extension ClassLoader,該加載器由sun.misc.Launcher$ExtClassLoader實現,它負責加載JDK\jre\lib\ext目錄中,或者由java.ext.dirs系統變量指定的路徑中的所有類庫(如javax.*開頭的類),開發者可以直接使用擴展類加載器。
應用程序類加載器:Application ClassLoader,該類加載器由sun.misc.Launcher$AppClassLoader來實現,它負責加載用戶類路徑(ClassPath)所指定的類,開發者可以直接使用該類加載器
類加載機制
全盤負責,當一個類加載器負責加載某個Class時,該Class所依賴的和引用的其他Class也將由該類加載器負責載入,除非顯示使用另外一個類加載器來載入
父類委托,先讓父類加載器試圖加載該類,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類
緩存機制,緩存機制將會保證所有加載過的Class都會被緩存,當程序中需要使用某個Class時,類加載器先從緩存區尋找該Class,只有緩存區不存在,系統才會讀取該類對應的二進制數據,並將其轉換成Class對象,存入緩存區。這就是為什麽修改了Class後,必須重啟JVM,程序的修改才會生效
三、jvm內存結構
主要關註點:jvm內存結構都是什麽、對象分配規則
jvm內存結構
JVM內存結構主要有三大塊:堆內存、方法區和棧。堆內存是JVM中最大的一塊由年輕代和老年代組成,而年輕代內存又被分成三部分,Eden(伊甸園)空間、From Survivor(
幸存0區)空間、To Survivor(幸存1區)空間,默認情況下年輕代按照8:1:1的比例來分配;方法區存儲類信息、常量、靜態變量等數據,是線程共享的區域,為與Java堆區分,方法區還有一個別名Non-Heap(非堆);棧又分為java虛擬機棧和本地方法棧主要用於方法的執行
方法區和堆是所有線程共享的內存區域;而java棧、本地方法棧和程序計數器是運行是線程私有的內存區域。
Java堆(Heap),是Java虛擬機所管理的內存中最大的一塊。Java堆是被所有線程共享的一塊內存區域,在虛擬機啟動時創建。此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這裏分配內存。
方法區(Method Area),方法區(Method Area)與Java堆一樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
程序計數器(Program Counter Register),程序計數器(Program Counter Register)是一塊較小的內存空間,它的作用可以看做是當前線程所執行的字節碼的行號指示器。
JVM棧(JVM Stacks),與程序計數器一樣,Java虛擬機棧(Java Virtual Machine Stacks)也是線程私有的,它的生命周期與線程相同。虛擬機棧描述的是Java方法執行的內存模型:每個方法被執行的時候都會同時創建一個棧幀(Stack Frame)用於存儲局部變量表、操作棧、動態鏈接、方法出口等信息。每一個方法被調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程。
本地方法棧(Native Method Stacks),本地方法棧(Native Method Stacks)與虛擬機棧所發揮的作用是非常相似的,其區別不過是虛擬機棧為虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則是為虛擬機使用到的Native方法服務。
對象分配規則
對象優先分配在Eden區,如果Eden區沒有足夠的空間時,虛擬機執行一次Minor GC。
大對象直接進入老年代(大對象是指需要大量連續內存空間的對象)。這樣做的目的是避免在Eden區和兩個Survivor區之間發生大量的內存拷貝(新生代采用復制算法收集內存)。
長期存活的對象進入老年代。虛擬機為每個對象定義了一個年齡計數器,如果對象經過了1次Minor GC那麽對象會進入Survivor區,之後每經過一次Minor GC那麽對象的年齡加1,知道達到閥值對象進入老年區。
動態判斷對象的年齡。如果Survivor區中相同年齡的所有對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象可以直接進入老年代。
空間分配擔保。每次進行Minor GC時,JVM會計算Survivor區移至老年區的對象的平均大小,如果這個值大於老年區的剩余值大小則進行一次Full GC,如果小於檢查HandlePromotionFailure設置,如果true則只進行Monitor GC,如果false則進行Full GC。
如何通過參數來控制個各個內存區域?參考:jvm系列(二):JVM內存結構
四、GC算法 垃圾回收
主要關註點:對象存活判斷、GC算法、垃圾回收器
對象存活判斷
判斷對象是否存活一般有兩種方式:
引用計數:每個對象有一個引用計數屬性,新增一個引用時計數加1,引用釋放時計數減1,計數為0時可以回收。此方法簡單,無法解決對象相互循環引用的問題。
可達性分析(Reachability Analysis):從GC Roots開始向下搜索,搜索所走過的路徑稱為引用鏈。當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的,不可達對象。
GC算法
GC最基礎的算法有三種:標記 -清除算法、復制算法、標記-壓縮算法,我們常用的垃圾回收器一般都采用分代收集算法。
標記 -清除算法,“標記-清除”(Mark-Sweep)算法,如它的名字一樣,算法分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成後統一回收掉所有被標記的對象。
復制算法,“復制”(Copying)的收集算法,它將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活著的對象復制到另外一塊上面,然後再把已使用過的內存空間一次清理掉。
標記-壓縮算法,標記過程仍然與“標記-清除”算法一樣,但後續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然後直接清理掉端邊界以外的內存。
分代收集算法,“分代收集”(Generational Collection)算法,把Java堆分為新生代和老年代,這樣就可以根據各個年代的特點采用最適當的收集算法。
垃圾回收器
Serial收集器,串行收集器是最古老,最穩定以及效率高的收集器,可能會產生較長的停頓,只使用一個線程去回收。
ParNew收集器,ParNew收集器其實就是Serial收集器的多線程版本。
Parallel收集器,Parallel Scavenge收集器類似ParNew收集器,Parallel收集器更關註系統的吞吐量。
Parallel Old 收集器,Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和“標記-整理”算法
CMS收集器,CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器。
G1收集器,G1 (Garbage-First)是一款面向服務器的垃圾收集器,主要針對配備多顆處理器及大容量內存的機器. 以極高概率滿足GC停頓時間要求的同時,還具備高吞吐量性能特征
GC算法和垃圾回收器算法圖解以及更詳細內容參考 jvm系列(三):GC算法 垃圾收集器
五、GC分析 命令調優
主要關註點:GC日誌分析、調優命令、調優工具
GC日誌分析
摘錄GC日誌一部分(前段為年輕代gc回收;後段為full gc回收):
2016-07-05T10:43:18.093+0800: 25.395: [GC [PSYoungGen: 274931K->10738K(274944K)] 371093K->147186K(450048K), 0.0668480 secs] [Times: user=0.17 sys=0.08, real=0.07 secs]
2016-07-05T10:43:18.160+0800: 25.462: [Full GC [PSYoungGen: 10738K->0K(274944K)] [ParOldGen: 136447K->140379K(302592K)] 147186K->140379K(577536K) [PSPermGen: 85411K->85376K(171008K)], 0.6763541 secs] [Times: user=1.75 sys=0.02, real=0.68 secs]
通過上面日誌分析得出,PSYoungGen、ParOldGen、PSPermGen屬於Parallel收集器。其中PSYoungGen表示gc回收前後年輕代的內存變化;ParOldGen表示gc回收前後老年代的內存變化;PSPermGen表示gc回收前後永久區的內存變化。young gc 主要是針對年輕代進行內存回收比較頻繁,耗時短;full gc 會對整個堆內存進行回城,耗時長,因此一般盡量減少full gc的次數
young gc 日誌:
Full GC日誌:
調優命令
Sun JDK監控和故障處理命令有jps jstat jmap jhat jstack jinfo
jps,JVM Process Status Tool,顯示指定系統內所有的HotSpot虛擬機進程。
jstat,JVM statistics Monitoring是用於監視虛擬機運行時狀態信息的命令,它可以顯示出虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據。
jmap,JVM Memory Map命令用於生成heap dump文件
jhat,JVM Heap Analysis Tool命令是與jmap搭配使用,用來分析jmap生成的dump,jhat內置了一個微型的HTTP/HTML服務器,生成dump的分析結果後,可以在瀏覽器中查看
jstack,用於生成java虛擬機當前時刻的線程快照。
jinfo,JVM Configuration info 這個命令作用是實時查看和調整虛擬機運行參數。
詳細的命令使用參考這裏jvm系列(四):jvm調優-命令篇
調優工具
常用調優工具分為兩類,jdk自帶監控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。
jconsole,Java Monitoring and Management Console是從java5開始,在JDK中自帶的java監控和管理控制臺,用於對JVM中內存,線程和類等的監控
jvisualvm,jdk自帶全能工具,可以分析內存快照、線程快照;監控內存變化、GC變化等。
MAT,Memory Analyzer Tool,一個基於Eclipse的內存分析工具,是一個快速、功能豐富的Java heap分析工具,它可以幫助我們查找內存泄漏和減少內存消耗
GChisto,一款專業分析gc日誌的工具
參考:JVM知識總覽
JVM知識點總結