JVM系列(八):jvm知識(shí)點(diǎn)總覽

在江湖中要練就絕世武功必須內(nèi)外兼?zhèn)洌畹恼惺胶蜕詈竦膬?nèi)功,武功的基礎(chǔ)是內(nèi)功。對(duì)于武功低(就像江南七怪)的人,招式更重要,因?yàn)樗麄儾荒芸績(jī)?nèi)功直接去傷人,只能靠招式,利刃上優(yōu)勢(shì)來取勝了,但是練到高手之后,內(nèi)功就更主要了。一個(gè)內(nèi)功低的人招式再奇妙也打不過一個(gè)內(nèi)功高的人。比如,你劍法再厲害,一劍刺過來,別人一掌打斷你的劍,你還怎么使劍法,你一掌打到一個(gè)武功高的人身上,那人沒什么事,卻把你震傷了,你還怎么打。同樣兩者也是相輔相成的,內(nèi)功深厚之后,原來普通的一招一式威力也會(huì)倍增。

對(duì)于搞開發(fā)的我們其實(shí)也是一樣,現(xiàn)在流行的框架越來越多,封裝的也越來越完善,各種框架可以搞定一切,幾乎不用關(guān)注底層的實(shí)現(xiàn),初級(jí)程序員只要熟悉基本的使用方法,便可以快速的開發(fā)上線;但對(duì)于高級(jí)程序員來講,內(nèi)功的修煉卻越發(fā)的重要,比如算法、設(shè)計(jì)模式、底層原理等,只有把這些基礎(chǔ)熟練之后,才能在開發(fā)過程中知其然知其所以然,出現(xiàn)問題時(shí)能快速定位到問題的本質(zhì)。

對(duì)于Java程序員來講,spring全家桶幾乎可以搞定一切,spring全家桶便是精妙的招式,jvm就是內(nèi)功心法很重要的一塊,線上出現(xiàn)性能問題,jvm調(diào)優(yōu)更是不可回避的問題。因此JVM基礎(chǔ)知識(shí)對(duì)于高級(jí)程序員的重要性不必言語。本篇文章會(huì)根據(jù)之前寫的jvm系列文章梳理出jvm需要關(guān)注的所有考察點(diǎn)。

jvm 總體梳理

jvm體系總體分四大塊:

  • 類的加載機(jī)制
  • jvm內(nèi)存結(jié)構(gòu)
  • GC算法 垃圾回收
  • GC分析 命令調(diào)優(yōu)

當(dāng)然這些知識(shí)點(diǎn)在之前的文章中都有詳細(xì)的介紹,這里只做主干的梳理

這里畫了一個(gè)思維導(dǎo)圖,將所有的知識(shí)點(diǎn)進(jìn)行了陳列,因?yàn)閳D比較大可以點(diǎn)擊右鍵下載了放大查看。

類的加載機(jī)制

主要關(guān)注點(diǎn):

  • 什么是類的加載
  • 類的生命周期
  • 類加載器
  • 雙親委派模型

什么是類的加載

類的加載指的是將類的.class文件中的二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中,將其放在運(yùn)行時(shí)數(shù)據(jù)區(qū)的方法區(qū)內(nèi),然后在堆區(qū)創(chuàng)建一個(gè)java.lang.Class對(duì)象,用來封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)。類的加載的最終產(chǎn)品是位于堆區(qū)中的Class對(duì)象,Class對(duì)象封裝了類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu),并且向Java程序員提供了訪問方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)的接口。

類的生命周期

類的生命周期包括這幾個(gè)部分,加載、連接、初始化、使用和卸載,其中前三部是類的加載的過程,如下圖;

image
  • 加載:查找并加載類的二進(jìn)制數(shù)據(jù),在Java堆中也創(chuàng)建一個(gè)java.lang.Class類的對(duì)象
  • 連接:連接又包含三塊內(nèi)容:驗(yàn)證、準(zhǔn)備、解析。1)驗(yàn)證:文件格式、元數(shù)據(jù)、字節(jié)碼、符號(hào)引用驗(yàn)證;2)準(zhǔn)備:為類的靜態(tài)變量分配內(nèi)存,并將其初始化為默認(rèn)值;3)解析:把類中的符號(hào)引用轉(zhuǎn)換為直接引用
  • 初始化:為類的靜態(tài)變量賦予正確的初始值
  • 使用:new出對(duì)象程序中使用
  • 卸載:執(zhí)行垃圾回收

幾個(gè)小問題?
1、JVM初始化步驟 ? 2、類初始化時(shí)機(jī) ?3、哪幾種情況下,Java虛擬機(jī)將結(jié)束生命周期?
答案參考這篇文章jvm系列(一):java類的加載機(jī)制

類加載器

類加載器
  • 啟動(dòng)類加載器:Bootstrap ClassLoader,負(fù)責(zé)加載存放在JDK\jre\lib(JDK代表JDK的安裝目錄,下同)下,或被-Xbootclasspath參數(shù)指定的路徑中的,并且能被虛擬機(jī)識(shí)別的類庫。
  • 擴(kuò)展類加載器:Extension ClassLoader,該加載器由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn),它負(fù)責(zé)加載DK\jre\lib\ext目錄中,或者由java.ext.dirs系統(tǒng)變量指定的路徑中的所有類庫(如javax.*開頭的類),開發(fā)者可以直接使用擴(kuò)展類加載器。
  • 應(yīng)用程序類加載器:Application ClassLoader,該類加載器由sun.misc.Launcher$AppClassLoader來實(shí)現(xiàn),它負(fù)責(zé)加載用戶類路徑(ClassPath)所指定的類,開發(fā)者可以直接使用該類加載器。

類加載機(jī)制

  • 全盤負(fù)責(zé),當(dāng)一個(gè)類加載器負(fù)責(zé)加載某個(gè)Class時(shí),該Class所依賴的和引用的其他Class也將由該類加載器負(fù)責(zé)載入,除非顯示使用另外一個(gè)類加載器來載入
  • 父類委托,先讓父類加載器試圖加載該類,只有在父類加載器無法加載該類時(shí)才嘗試從自己的類路徑中加載該類
  • 緩存機(jī)制,緩存機(jī)制將會(huì)保證所有加載過的Class都會(huì)被緩存,當(dāng)程序中需要使用某個(gè)Class時(shí),類加載器先從緩存區(qū)尋找該Class,只有緩存區(qū)不存在,系統(tǒng)才會(huì)讀取該類對(duì)應(yīng)的二進(jìn)制數(shù)據(jù),并將其轉(zhuǎn)換成Class對(duì)象,存入緩存區(qū)。這就是為什么修改了Class后,必須重啟JVM,程序的修改才會(huì)生效

jvm內(nèi)存結(jié)構(gòu)

主要關(guān)注點(diǎn):

  • jvm內(nèi)存結(jié)構(gòu)都是什么
  • 對(duì)象分配規(guī)則

jvm內(nèi)存結(jié)構(gòu)

jvm內(nèi)存結(jié)構(gòu)

方法區(qū)和堆是所有線程共享的內(nèi)存區(qū)域;而java棧、本地方法棧和程序計(jì)數(shù)器是運(yùn)行是線程私有的內(nèi)存區(qū)域。

  • Java堆(Heap),是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊。Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存。
  • 方法區(qū)(Method Area),方法區(qū)(Method Area)與Java堆一樣,是各個(gè)線程共享的內(nèi)存區(qū)域,它用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。
  • 程序計(jì)數(shù)器(Program Counter Register),程序計(jì)數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器。
  • JVM棧(JVM Stacks),與程序計(jì)數(shù)器一樣,Java虛擬機(jī)棧(Java Virtual Machine Stacks)也是線程私有的,它的生命周期與線程相同。虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法被執(zhí)行的時(shí)候都會(huì)同時(shí)創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲(chǔ)局部變量表、操作棧、動(dòng)態(tài)鏈接、方法出口等信息。每一個(gè)方法被調(diào)用直至執(zhí)行完成的過程,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過程。
  • 本地方法棧(Native Method Stacks),本地方法棧(Native Method Stacks)與虛擬機(jī)棧所發(fā)揮的作用是非常相似的,其區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧則是為虛擬機(jī)使用到的Native方法服務(wù)。

對(duì)象分配規(guī)則

  • 對(duì)象優(yōu)先分配在Eden區(qū),如果Eden區(qū)沒有足夠的空間時(shí),虛擬機(jī)執(zhí)行一次Minor GC。
  • 大對(duì)象直接進(jìn)入老年代(大對(duì)象是指需要大量連續(xù)內(nèi)存空間的對(duì)象)。這樣做的目的是避免在Eden區(qū)和兩個(gè)Survivor區(qū)之間發(fā)生大量的內(nèi)存拷貝(新生代采用復(fù)制算法收集內(nèi)存)。
  • 長(zhǎng)期存活的對(duì)象進(jìn)入老年代。虛擬機(jī)為每個(gè)對(duì)象定義了一個(gè)年齡計(jì)數(shù)器,如果對(duì)象經(jīng)過了1次Minor GC那么對(duì)象會(huì)進(jìn)入Survivor區(qū),之后每經(jīng)過一次Minor GC那么對(duì)象的年齡加1,知道達(dá)到閥值對(duì)象進(jìn)入老年區(qū)。
  • 動(dòng)態(tài)判斷對(duì)象的年齡。如果Survivor區(qū)中相同年齡的所有對(duì)象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對(duì)象可以直接進(jìn)入老年代。
  • 空間分配擔(dān)保。每次進(jìn)行Minor GC時(shí),JVM會(huì)計(jì)算Survivor區(qū)移至老年區(qū)的對(duì)象的平均大小,如果這個(gè)值大于老年區(qū)的剩余值大小則進(jìn)行一次Full GC,如果小于檢查HandlePromotionFailure設(shè)置,如果true則只進(jìn)行Monitor GC,如果false則進(jìn)行Full GC。

如何通過參數(shù)來控制個(gè)各個(gè)內(nèi)存區(qū)域
參考此文章:jvm系列(二):JVM內(nèi)存結(jié)構(gòu)

GC算法 垃圾回收

主要關(guān)注點(diǎn):

  • 對(duì)象存活判斷
  • GC算法
  • 垃圾回收器

對(duì)象存活判斷

判斷對(duì)象是否存活一般有兩種方式:

  • 引用計(jì)數(shù):每個(gè)對(duì)象有一個(gè)引用計(jì)數(shù)屬性,新增一個(gè)引用時(shí)計(jì)數(shù)加1,引用釋放時(shí)計(jì)數(shù)減1,計(jì)數(shù)為0時(shí)可以回收。此方法簡(jiǎn)單,無法解決對(duì)象相互循環(huán)引用的問題。
  • 可達(dá)性分析(Reachability Analysis):從GC Roots開始向下搜索,搜索所走過的路徑稱為引用鏈。當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈相連時(shí),則證明此對(duì)象是不可用的,不可達(dá)對(duì)象。

GC算法

GC最基礎(chǔ)的算法有三種:標(biāo)記 -清除算法、復(fù)制算法、標(biāo)記-壓縮算法,我們常用的垃圾回收器一般都采用分代收集算法。

  • 標(biāo)記 -清除算法,“標(biāo)記-清除”(Mark-Sweep)算法,如它的名字一樣,算法分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對(duì)象。
  • 復(fù)制算法,“復(fù)制”(Copying)的收集算法,它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另外一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉。
  • 標(biāo)記-壓縮算法,標(biāo)記過程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存。
  • 分代收集算法,“分代收集”(Generational Collection)算法,把Java堆分為新生代和老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?/li>

垃圾回收器

  • Serial收集器,串行收集器是最古老,最穩(wěn)定以及效率高的收集器,可能會(huì)產(chǎn)生較長(zhǎng)的停頓,只使用一個(gè)線程去回收。
  • ParNew收集器,ParNew收集器其實(shí)就是Serial收集器的多線程版本。
  • Parallel收集器,Parallel Scavenge收集器類似ParNew收集器,Parallel收集器更關(guān)注系統(tǒng)的吞吐量。
  • Parallel Old 收集器,Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和“標(biāo)記-整理”算法。
  • CMS收集器,CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。
  • G1收集器,G1 (Garbage-First)是一款面向服務(wù)器的垃圾收集器,主要針對(duì)配備多顆處理器及大容量?jī)?nèi)存的機(jī)器,以極高概率滿足GC停頓時(shí)間要求的同時(shí),還具備高吞吐量性能特征。

GC算法和垃圾回收器算法圖解以及更詳細(xì)內(nèi)容參考 jvm系列(三):GC算法 垃圾收集器

GC分析 命令調(diào)優(yōu)

主要關(guān)注點(diǎn):

  • GC日志分析
  • 調(diào)優(yōu)命令
  • 調(diào)優(yōu)工具

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回收前后年輕代的內(nèi)存變化;ParOldGen表示gc回收前后老年代的內(nèi)存變化;PSPermGen表示gc回收前后永久區(qū)的內(nèi)存變化。young gc 主要是針對(duì)年輕代進(jìn)行內(nèi)存回收比較頻繁,耗時(shí)短;full gc 會(huì)對(duì)整個(gè)堆內(nèi)存進(jìn)行回城,耗時(shí)長(zhǎng),因此一般盡量減少full gc的次數(shù)。

Young GC日志:

Young GC日志

Full GC日志:

Full GC日志

調(diào)優(yōu)命令

Sun JDK監(jiān)控和故障處理命令有jps、jstatjmap、jhatjstack、jinfo

  • jps,JVM Process Status Tool,顯示指定系統(tǒng)內(nèi)所有的HotSpot虛擬機(jī)進(jìn)程。
  • jstat,JVM statistics Monitoring是用于監(jiān)視虛擬機(jī)運(yùn)行時(shí)狀態(tài)信息的命令,它可以顯示出虛擬機(jī)進(jìn)程中的類裝載、內(nèi)存、垃圾收集、JIT編譯等運(yùn)行數(shù)據(jù)。
  • jmap,JVM Memory Map命令用于生成heap dump文件。
  • jhat,JVM Heap Analysis Tool命令是與jmap搭配使用,用來分析jmap生成的dump,jhat內(nèi)置了一個(gè)微型的HTTP/HTML服務(wù)器,生成dump的分析結(jié)果后,可以在瀏覽器中查看。
  • jstack,用于生成java虛擬機(jī)當(dāng)前時(shí)刻的線程快照。
  • jinfo,JVM Configuration info 這個(gè)命令作用是實(shí)時(shí)查看和調(diào)整虛擬機(jī)運(yùn)行參數(shù)。

詳細(xì)的命令使用參考這里jvm系列(四):jvm調(diào)優(yōu)-命令篇

調(diào)優(yōu)工具

常用調(diào)優(yōu)工具分為兩類,jdk自帶監(jiān)控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。

  • jconsole,Java Monitoring and Management Console是從java5開始,在JDK中自帶的java監(jiān)控和管理控制臺(tái),用于對(duì)JVM中內(nèi)存,線程和類等的監(jiān)控。
  • jvisualvm,jdk自帶全能工具,可以分析內(nèi)存快照、線程快照;監(jiān)控內(nèi)存變化、GC變化等。
  • MAT,Memory Analyzer Tool,一個(gè)基于Eclipse的內(nèi)存分析工具,是一個(gè)快速、功能豐富的Java heap分析工具,它可以幫助我們查找內(nèi)存泄漏和減少內(nèi)存消耗。
  • GChisto,一款專業(yè)分析gc日志的工具。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言: 在遨游了一番 Java Web 的世界之后,發(fā)現(xiàn)了自己的一些缺失,所以就著一篇深度好文:知名互聯(lián)網(wǎng)公司校招...
    我沒有三顆心臟閱讀 10,642評(píng)論 15 121
  • 原文閱讀 前言 這段時(shí)間懈怠了,罪過! 最近看到有同事也開始用上了微信公眾號(hào)寫博客了,挺好的~給他們點(diǎn)贊,這博客我...
    碼農(nóng)戲碼閱讀 6,150評(píng)論 2 31
  • 就這樣又開始一個(gè)人生活著,曾經(jīng)為了她是多么上心,如今只感覺傷心可惜,還是我不夠好。每天雖然偶爾會(huì)感覺孤獨(dú),但是,自...
    魂歸瀟湘閱讀 130評(píng)論 0 0
  • 人生,就是一場(chǎng)場(chǎng)遇見。你,遇見我;我,遇見你。 ——題...
    隱露閱讀 343評(píng)論 5 4
  • “這個(gè)世界上既有妖、鬼、神、魔。 這個(gè)世界上也有糖芽般甜澀,柔韌真實(shí),均勻的,模糊繁茂而又頑固的愛?!?/div>
    雜芰_閱讀 191評(píng)論 0 0

友情鏈接更多精彩內(nèi)容