一篇文章徹底搞懂Java虛擬機

概念:

虛擬機:指以軟件的方式模擬具有完整硬件系統(tǒng)功能列林、運行在一個完全隔離環(huán)境中的完整計算機系統(tǒng) ,是物理機的軟件實現(xiàn)酪惭。常用的虛擬機有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)成字節(jié)碼,由jre運行颂翼。jre由java虛擬機(jvm)實現(xiàn)晃洒。Jvm分析字節(jié)碼,后解釋并執(zhí)行朦乏。

JVM由三個主要的子系統(tǒng)構成:

  • 類加載器子系統(tǒng)
  • 運行時數(shù)據(jù)區(qū)(內(nèi)存)
  • 執(zhí)行引擎

類加載器子系統(tǒng)

類裝載包括了加載球及,連接(驗證、準備呻疹、解析(可選))吃引,初始化。其中類加載工作由ClassLoader及其子類負責。

  • 加載:在硬盤上查找并通過IO讀入字節(jié)碼文件
  • 連接:執(zhí)行校驗际歼、準備惶翻、解析(可選)步驟
  • 校驗:校驗字節(jié)碼文件的正確性
  • 準備:給類的靜態(tài)變量分配內(nèi)存,并賦予默認值
  • 解析:將符號引用轉為直接引用鹅心,類裝載器裝入類所引用的其他所有類
  • 初始化:對類的靜態(tài)變量初始化為指定的值吕粗,執(zhí)行靜態(tài)代碼塊

類加載器體系結構

  • 啟動類加載器:負責加載JRE的核心類庫,如jre目標下的rt.jar,charsets.jar等.
  • 擴展類加載器:負責加載JRE擴展目錄ext中JAR類包
  • 系統(tǒng)類加載器:負責加載ClassPath路徑下的類包
  • 用戶自定義加載器:負責加載用戶自定義路徑下的類包

類加載機制(雙親委派)

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

運行時數(shù)據(jù)區(qū)

堆(Java堆)

虛擬機啟動時創(chuàng)建桃熄,用于存放對象實例先口,幾乎所有的對象(包含常量池)都在堆上分配內(nèi)存,當對象無法再該空間申請到內(nèi)存時將拋出OutOfMemoryError異常瞳收。同時也是垃圾收集器管理的主要區(qū)域碉京。可通過 -Xmx –Xms 參數(shù)來分別指定最大堆和最小堆螟深。線程共享谐宙。

棧(Java棧)

是java方法執(zhí)行的內(nèi)存模型,為虛擬機執(zhí)行java方法界弧,每個方法在執(zhí)行的同時都會創(chuàng)建一個棧幀(用于存儲局部變量表凡蜻,操作數(shù)棧,動態(tài)鏈接垢箕,方法出口等信息)划栓。線程獨占。

Jvm對該區(qū)域規(guī)范了兩種異常:

  • 線程請求的棧深度大于虛擬機棧所允許的深度舰讹,將拋出StackOverFlowError異常茅姜。
  • 若虛擬機棧可動態(tài)擴展月匣,當無法申請到足夠內(nèi)存空間時將拋出OutOfMemoryError。通過jvm參數(shù)–Xss指定椃茏耍空間锄开,空間大小決定函數(shù)調用的深度。

本地方法棧

為虛擬機執(zhí)行native方法称诗,其他規(guī)范與java棧類似萍悴。不同類型的虛擬機對該區(qū)域可自由實現(xiàn)。線程獨占。

PC寄存器(程序計數(shù)器)

用來存儲待執(zhí)行指令的地址癣诱。分支计维,循環(huán),跳轉撕予,異常處理鲫惶,線程恢復等功能都需要依賴pc寄存器。線程獨占实抡。

若線程執(zhí)行的是一個java方法欠母,則pc寄存器中保存的是待執(zhí)行指令的地址。若執(zhí)行的是一個native方法吆寨,則pc寄存器中為空赏淌。

元數(shù)據(jù)區(qū)

元數(shù)據(jù)區(qū)取代了永久代,本質和永久代類似啄清,都是對JVM規(guī)范中方法區(qū)的實現(xiàn)六水,區(qū)別在于元數(shù)據(jù)區(qū)并不在虛擬機中,而是使用本地內(nèi)存辣卒。元數(shù)據(jù)區(qū)在頻繁使用掷贾,也會發(fā)生OutOfMemory異常。

元數(shù)據(jù)區(qū)的動態(tài)擴展添寺,默認–XX:MetaspaceSize值為21MB的高水位線胯盯。一旦觸及則Full GC將被觸發(fā)并卸載沒有用的類(類對應的類加載器不再存活),然后高水位線將會重置计露。新的高水位線的值取決于GC后釋放的元空間博脑。如果釋放的空間少,這個高水位線則上升票罐。如果釋放空間過多叉趣,則高水位線下降。

執(zhí)行引擎

執(zhí)行引擎讀取運行時數(shù)據(jù)區(qū)的字節(jié)碼并逐個執(zhí)行

  • 解釋器:解釋器更快地解釋字節(jié)碼,但執(zhí)行緩慢,解釋一句執(zhí)行一句该押。

  • JIT編譯器:JIT編譯器消除了解釋器的缺點疗杉。執(zhí)行引擎通過解釋器轉換字節(jié)碼,當它發(fā)現(xiàn)重復的代碼時蚕礼,將使用JIT編譯器烟具,它編譯整個字節(jié)碼并將其更改為本地代碼。這個本地代碼將直接用于重復的方法調用奠蹬,這提高了系統(tǒng)的性能朝聋。
    JIT的構成組件為:

    • 中間代碼生成器(Intermediate Code Generator):生成中間代碼 。
    • 代碼優(yōu)化器(Code Optimizer):負責優(yōu)化上面生成的中間代碼 囤躁。
    • 目標代碼生成器(Target Code Generator):負責生成機器代碼或本地代碼 冀痕。
    • 分析器(Profiler):一個特殊組件荔睹,負責查找熱點(被多次調用的方法)
  • 垃圾收集器:收集和刪除未引用的對象。程序可調用System.gc()觸發(fā)垃圾收集言蛇,但不能保證執(zhí)行僻他。

    • 本地方法接口(JNI):JNI將與本機方法庫進行交互,并提供執(zhí)行引擎所需的本機庫腊尚。
    • 本地方法庫:執(zhí)行引擎所需的本機庫的集合吨拗。

垃圾收集(GC:Garbage Collection)

  • 如何識別垃圾,判定對象是否可被回收跟伏?

    • 引用計數(shù)法:給每個對象添加一個計數(shù)器丢胚,當有地方引用該對象時計數(shù)器加1,當引用失效時計數(shù)器減1受扳。用對象計數(shù)器是否為0來判斷對象是否可被回收携龟。缺點:無法解決循環(huán)引用的問題
    • 根搜索算法:也稱可達性分析法,通過“GC ROOTs”的對象作為搜索起始點勘高,通過引用向下搜索峡蟋,所走過的路徑稱為引用鏈。通過對象是否有到達引用鏈的路徑來判斷對象是否可被回收(可作為GC ROOTs的對象:虛擬機棧中引用的對象华望,方法區(qū)中類靜態(tài)屬性引用的對象蕊蝗,方法區(qū)中常量引用的對象,本地方法棧中JNI引用的對象)
  • Java 中的堆是 GC 收集垃圾的主要區(qū)域赖舟,GC 分為兩種:Minor GC蓬戚、Full GC ( 或稱為 Major GC )。

    • Minor GC:新生代(Young Gen)空間不足時觸發(fā)收集宾抓,由于Java 中的大部分對象通常不需長久存活子漩,新生代是GC收集頻繁區(qū)域,所以采用復制算法石洗。
    • Full GC:老年代(Old Gen )空間不足或元空間達到高水位線執(zhí)行收集動作幢泼,由于存放大對象及長久存活下的對象,占用內(nèi)存空間大讲衫,回收效率低缕棵,所以采用標記-清除算法。

GC算法

按照回收策略劃分為:標記-清除算法涉兽,標記-整理算法招驴,復制算法。

  • 標記-清除算法:分為兩階段“標記”和“清除”枷畏。首先標記出哪些對象可被回收忽匈,在標記完成之后統(tǒng)一回收所有被標記的對象所占用的內(nèi)存空間。不足之處:1.無法處理循環(huán)引用的問題2.效率不高3.產(chǎn)生大量內(nèi)存碎片(ps:空間碎片太多可能會導致以后在分配大對象的時候而無法申請到足夠的連續(xù)內(nèi)存空間矿辽,導致提前觸發(fā)新一輪gc)
  • 標記-整理算法:分為兩階段“標記”和“整理”丹允。首先標記出哪些對象可被回收,在標記完成后袋倔,將對象向一端移動雕蔽,然后直接清理掉邊界以外的內(nèi)存。
  • 復制算法:把內(nèi)存空間劃為兩個相等的區(qū)域宾娜,每次只使用其中一個區(qū)域批狐。gc時遍歷當前使用區(qū)域,把正在使用中的對象復制到另外一個區(qū)域中前塔。算法每次只處理正在使用中的對象嚣艇,因此復制成本比較小,同時復制過去以后還能進行相應的內(nèi)存整理华弓,不會出現(xiàn)“碎片”問題食零。不足之處:1.內(nèi)存利用率問題2.在對象存活率較高時,其效率會變低寂屏。

按分區(qū)對待可分為:增量收集算法贰谣,分代收集算法

  • 增量收集:實時垃圾回收算法,即:在應用進行的同時進行垃圾回收迁霎,理論上可以解決傳統(tǒng)分代方式帶來的問題吱抚。增量收集把對堆空間劃分成一系列內(nèi)存塊,使用時先使用其中一部分考廉,垃圾收集時把之前用掉的部分中的存活對象再放到后面沒有用的空間中秘豹,這樣可以實現(xiàn)一直邊使用邊收集的效果,避免了傳統(tǒng)分代方式整個使用完了再暫停的回收的情況昌粤。
  • 分代收集:(商用默認)基于對象生命周期劃分為新生代既绕、老年代、元空間婚苹,對不同生命周期的對象使用不同的算法進行回收岸更。

按系統(tǒng)線程可分為:串行收集算法,并行收集算法膊升,并發(fā)收集算法

  • 串行收集:使用單線程處理垃圾回收工作怎炊,實現(xiàn)容易,效率較高廓译。不足之處:1.無法發(fā)揮多處理器的優(yōu)勢 2.需要暫停用戶線程
  • 并行收集:使用多線程處理垃圾回收工作评肆,速度快,效率高非区。理論上CPU數(shù)目越多瓜挽,越能體現(xiàn)出并行收集器的優(yōu)勢。不足之處:需要暫停用戶線程
  • 并發(fā)收集:垃圾線程與用戶線程同時工作征绸。系統(tǒng)在垃圾回收時不需要暫停用戶線程

GC收集器

垃圾收集算法是內(nèi)存回收的理論基礎久橙,而垃圾收集器就是內(nèi)存回收的具體實現(xiàn)掏婶。

  • Serial 收集器主要針對新生代的收集,是最基本最古老的收集器遏插,它是單線程收集器忱反,工作時必須暫停所有用戶線程。該收集器采用復制算法祝拯。
    Serial Old收集器主要針對老年代收集甚带,采用標記-整理算法,實現(xiàn)簡單高效佳头,但會停頓鹰贵。
  • ParNew收集器是Serial的多線程版本,針對新生代采用復制算法使用多線程進行垃圾收集(并行收集器康嘉,響應優(yōu)先)碉输。

  • Parallel Scavenge采用復制算法針對新生代的多線程收集器(并行收集器,吞吐優(yōu)先)凄鼻±吧可控制吞吐量和停頓時間,即吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間+垃圾收集時間)块蚌。
    Parallel Old收集器是Parallel Scavenge收集器的老年代版本(并行收集器)闰非,使用多線程和標記-整理算法。

  • CMS(Current MarkSweep)收集器針對老年代峭范,是一種以獲取最短回收停頓時間為目標的收集器财松,它是一種并發(fā)收集器,采用的是標記-清除算法纱控。
  • G1的新生代類似于ParNew辆毡,采用復制算法算法,當新生代占用達到一定比例的時候甜害,開始收集舶掖。老年代類似于CMS,不同點是采用標記-整理算法尔店。
    G1因此它是一款并行與并發(fā)收集器眨攘,能充分利用多CPU、多核環(huán)境嚣州。并且它能建立可預測的停頓時間模型鲫售。

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

  • 空間整合,G1收集器采用標記-整理算法该肴,不會產(chǎn)生內(nèi)存空間碎片情竹。分配大對象(直接進Humongous區(qū),專門存放短期巨型對象匀哄,不用直接進老年代秦效,避免Full GC的大量開銷)不會因為無法找到連續(xù)空間而提前觸發(fā)下一次GC雏蛮。(年青代拷貝、老年代轉移對象無空閑分區(qū)棉安、巨型對象無連續(xù)分區(qū)時觸發(fā)Full GC底扳,開銷極大應該避免)

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

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

收集器常用組合

JVM性能調優(yōu)思路

常見異常

  • StackOverflowError:(棧溢出)
  • OutOfMemoryError: Java heap space(堆空間不足)
  • OutOfMemoryError: GC overhead limit exceeded (GC花費的時間超過 98%, 并且GC回收的內(nèi)存少于 2%)

GC參數(shù)

  • 堆棧設置
  • -Xss:每個線程的棧大小
  • -Xms:初始堆大小,默認物理內(nèi)存的1/64
  • -Xmx:最大堆大小尘颓,默認物理內(nèi)存的1/4
  • -Xmn:新生代大小
  • -XX:NewSize:設置新生代初始大小
  • -XX:NewRatio:默認2表示新生代占年老代的1/2走触,占整個堆內(nèi)存的1/3。
  • -XX:SurvivorRatio:默認8表示一個survivor區(qū)占用1/8的Eden內(nèi)存疤苹,即1/10的新生代內(nèi)存互广。
  • -XX:MaxMetaspaceSize:設置元空間最大允許大小,默認不受限制卧土,JVM Metaspace會進行動態(tài)擴展惫皱。

并行收集器設置

  • -XX:ParallelGCThreads:設置并行收集器收集時使用的CPU數(shù)。并行收集線程數(shù)尤莺。
  • -XX:MaxGCPauseMillis:設置并行收集最大暫停時間
  • -XX:GCTimeRatio:設置垃圾回收時間占程序運行時間的百分比旅敷。公式為1/(1+n)

CMS收集器設置

  • -XX:+UseConcMarkSweepGC:設置CMS并發(fā)收集器
  • -XX:+CMSIncrementalMode:設置為增量模式。適用于單CPU情況颤霎。
  • -XX:ParallelGCThreads:設置并發(fā)收集器新生代收集方式為并行收集時媳谁,使用的CPU數(shù)。并行收集線程數(shù)捷绑。
  • -XX:CMSFullGCsBeforeCompaction:設定進行多少次CMS垃圾回收后韩脑,進行一次內(nèi)存壓縮
  • CMSClassUnloadingEnabled:允許對類元數(shù)據(jù)進行回收
  • -XX:UseCMSInitiatingOccupancyOnly:表示只在到達閥值的時候,才進行CMS回收
  • -XX:+CMSIncrementalMode:設置為增量模式粹污。適用于單CPU情況
  • -XX:ParallelCMSThreads:設定CMS的線程數(shù)量
  • -XX:CMSInitiatingOccupancyFraction:設置CMS收集器在老年代空間被使用多少后觸發(fā)
  • -XX:+UseCMSCompactAtFullCollection:設置CMS收集器在完成垃圾收集后是否要進行一次內(nèi)存碎片的整理

垃圾回收統(tǒng)計信息

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

G1收集器設置

  • -XX:+UseG1GC:使用G1收集器
  • -XX:ParallelGCThreads:指定GC工作的線程數(shù)量
  • -XX:G1HeapRegionSize:指定分區(qū)大小(1MB~32MB段多,且必須是2的冪),默認將整堆劃分為2048個分區(qū)
  • -XX:GCTimeRatio:吞吐量大小壮吩,0-100的整數(shù)(默認9)进苍,值為n則系統(tǒng)將花費不超過1/(1+n)的時間用于垃圾收集
  • 目標暫停時間(默認200ms)
  • -XX:G1NewSizePercent:新生代內(nèi)存初始空間(默認整堆5%)
  • -XX:G1MaxNewSizePercent:新生代內(nèi)存最大空間
  • 填充容量(默認50%)
  • -XX:MaxTenuringThreshold:最大任期閾值(默認15)
  • -XX:InitiatingHeapOccupancyPercen:老年代占用空間超過整堆比IHOP閾值(默認45%),超過則執(zhí)行混合收集
  • -XX:G1HeapWastePercent:堆廢物百分比(默認5%)
  • -XX:G1MixedGCCountTarget:參數(shù)混合周期的最大總次數(shù)(默認8)

收集器設置

  • -XX:+UseSerialGC:設置串行收集器
  • -XX:+UseParallelGC:設置并行收集器
  • -XX:+UseParallelOldGC:老年代使用并行回收收集器
  • -XX:+UseParNewGC:在新生代使用并行收集器
  • -XX:+UseParalledlOldGC:設置并行老年代收集器
  • UseConcMarkSweepGC:設置CMS并發(fā)收集器
  • -XX:+UseG1GC:設置G1收集器
  • -XX:ParallelGCThreads:設置用于垃圾回收的線程數(shù)

性能分析和監(jiān)控工具

  • Jps:虛擬機進程狀況工具
  • 虛擬機統(tǒng)計信息監(jiān)視工具
  • 虛擬機配置信息工具
  • Jmap:內(nèi)存映像工具
  • Jhat:虛擬機堆轉儲快照分析工具
  • Jstack:堆棧跟蹤工具
  • JConsole:java監(jiān)視與管理控制臺
  • VisualVM:故障處理工具

點關注加缘,不迷路;持續(xù)更新Java架構相關技術及資訊熱文>醢 <鸷辍!

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末杠人,一起剝皮案震驚了整個濱河市勋乾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嗡善,老刑警劉巖辑莫,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異罩引,居然都是意外死亡各吨,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門袁铐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揭蜒,“玉大人,你說我怎么就攤上這事剔桨√敫” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵领炫,是天一觀的道長偶垮。 經(jīng)常有香客問我,道長帝洪,這世上最難降的妖魔是什么似舵? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮葱峡,結果婚禮上砚哗,老公的妹妹穿的比我還像新娘。我一直安慰自己砰奕,他們只是感情好蛛芥,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著军援,像睡著了一般仅淑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胸哥,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天涯竟,我揣著相機與錄音,去河邊找鬼。 笑死庐船,一個胖子當著我的面吹牛银酬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播筐钟,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼揩瞪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了篓冲?” 一聲冷哼從身側響起李破,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎纹因,沒想到半個月后喷屋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡瞭恰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了狱庇。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惊畏。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖密任,靈堂內(nèi)的尸體忽然破棺而出颜启,到底是詐尸還是另有隱情,我是刑警寧澤浪讳,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布缰盏,位于F島的核電站,受9級特大地震影響淹遵,放射性物質發(fā)生泄漏口猜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一透揣、第九天 我趴在偏房一處隱蔽的房頂上張望济炎。 院中可真熱鬧,春花似錦辐真、人聲如沸须尚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耐床。三九已至,卻和暖如春楔脯,著一層夾襖步出監(jiān)牢的瞬間撩轰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钧敞,地道東北人蜡豹。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像溉苛,于是被迫代替她去往敵國和親镜廉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容