一、Java平臺(tái)的結(jié)構(gòu)圖
二、JVM與JRE、JDK關(guān)系
JVM:Java Virtual Machine負(fù)責(zé)執(zhí)行符合規(guī)范的Class文件
JRE:Java Runtime Environment(java運(yùn)行環(huán)境),包含JVM和類庫(kù)
JDK:Java Development Kit(java開(kāi)發(fā)工具包)包含JRE和開(kāi)發(fā)工具包
三、JVM所處的位置
四、java運(yùn)行過(guò)程
即時(shí)編譯器(JIT)
五、jvm結(jié)構(gòu)
-
方法區(qū)(method area)
- 類信息、常量、精通變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。方法區(qū)是全局共享的,在一定條件下它也會(huì)被GC。
- 當(dāng)方法區(qū)使用的內(nèi)存超過(guò)它允許的大小時(shí),就會(huì)拋出OutOfMemory:PermGen Space異常。
- JVM方法區(qū)的相關(guān)參數(shù),最小值:--XX:PermSize;最大值 --XX:MaxPermSize。
-
堆(heap)
- 所有對(duì)象實(shí)例及數(shù)組,堆區(qū)是理解JavaGC機(jī)制最重要的區(qū)域。在JVM所管理的內(nèi)存中,堆區(qū)是最大的一塊,堆區(qū)也是JavaGC機(jī)制所管理的主要內(nèi)存區(qū)域,堆區(qū)由所有線程共享,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。堆區(qū)用來(lái)存儲(chǔ)對(duì)象實(shí)例及數(shù)組值,可以認(rèn)為java中所有通過(guò)new創(chuàng)建的對(duì)象都在此分配。
- 年輕代(Young Generation),對(duì)象在被創(chuàng)建時(shí),內(nèi)存首先是在年輕代進(jìn)行分配(注意,大對(duì)象可以直接在老年代分配)。當(dāng)年輕代需要回收時(shí)會(huì)觸發(fā)Minor GC(也稱作Young GC)。
如果在執(zhí)行垃圾回收之后,仍沒(méi)有足夠的內(nèi)存分配,也不能再擴(kuò)展,將會(huì)拋出OutOfMemoryError:Java Heap Space異常。 - 老年代(Old Generation)老年代用于存放在年輕代中經(jīng)多次垃圾回收仍然存活的對(duì)象,可以理解為比較老一點(diǎn)的對(duì)象。當(dāng)老年代滿了的時(shí)候就需要對(duì)老年代進(jìn)行垃圾回收,老年代的垃圾回收稱作Major GC(也稱作Full GC)。
-
程序計(jì)數(shù)器(Program Counter Register)
- 是一塊較小的內(nèi)存空間,指向當(dāng)時(shí)線程正在執(zhí)行的字節(jié)碼指令的地址
- 此內(nèi)存區(qū)域是唯一一個(gè)在Java虛擬機(jī)規(guī)范中沒(méi)有規(guī)定任何OutMemoryError情況的區(qū)域
-
本地方法棧(Native Method Stacks)
- 虛擬機(jī)用到的本地方法,與虛擬機(jī)發(fā)揮的作用是非常相似的,其區(qū)別不過(guò)是虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧則是為虛擬機(jī)使用到的Native方法服務(wù)。
- 與虛擬機(jī)棧一樣,本地方法區(qū)域也會(huì)拋出StackOverflowError和OutOfMemoryError異常
-
java虛擬機(jī)棧(Java Virtual Machine Stacks)
- 當(dāng)前線程運(yùn)行方法時(shí)需要用到的局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等。
- 在Java虛擬機(jī)規(guī)范中,對(duì)這個(gè)區(qū)域規(guī)定了兩種異常情況:如果線程請(qǐng)求的棧深度大于虛擬機(jī)允許的深度,將拋出StackOverflowError異常;如果虛擬機(jī)棧可以動(dòng)態(tài)擴(kuò)展,當(dāng)擴(kuò)展時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存時(shí)會(huì)拋出OutOfMemoryError異常
其中,方法區(qū)和堆是所有線程共享的。
五、jvm內(nèi)存模型
可見(jiàn)性
原子性
六、jvm類加載機(jī)制
虛擬機(jī)把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的java類型,這就是類的加載機(jī)制。
在java語(yǔ)言里面,類型的加載和連接過(guò)程都是在程序運(yùn)行期完成的。
加載、驗(yàn)證、準(zhǔn)備、初始化和卸載這五個(gè)階段的順序是確定的,類的加載過(guò)程必須按照這種順序按部就班的開(kāi)始,而解析階段則不一定:它在某些情況下可以在初始化階段之后再開(kāi)始,這是為了支持java語(yǔ)言的運(yùn)行時(shí)綁定,這些階段通常都是互相交叉低混合式進(jìn)行的。
-
類的加載過(guò)程
- 加載:類加載過(guò)程的第一階段,在加載階段,虛擬機(jī)需要完成以下三件事情
- 通過(guò)一個(gè)類的全限定名來(lái)獲取定義此類的二進(jìn)制字節(jié)流。
- 講這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)
- 在java堆中生成一個(gè)代表這個(gè)類的ava.lang.Class對(duì)象,作為方法區(qū)這些數(shù)據(jù)的訪問(wèn)入口
- 加載:類加載過(guò)程的第一階段,在加載階段,虛擬機(jī)需要完成以下三件事情
-
驗(yàn)證:驗(yàn)證是連接階段的第一步,這一階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)的安全
- 文件格式驗(yàn)證
- 元數(shù)據(jù)驗(yàn)證
- 字節(jié)碼驗(yàn)證
- 符號(hào)引用驗(yàn)證
準(zhǔn)備:為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些內(nèi)存都將在方法區(qū)中進(jìn)行分配。
-
解析:解析階段是虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用的過(guò)程
- 類或接口的解析
- 字段解析
- 類方法解析
- 接口方法解析
初始化:類初始化階段是類加載過(guò)程的最后一步,這個(gè)階段才真正開(kāi)始執(zhí)行類定義的java程序代碼(字節(jié)碼)
七、jvm類加載器
類與類加載器:類加載器用于實(shí)現(xiàn)類的加載動(dòng)作,任意一個(gè)類都需要由加載它的類加載器和這個(gè)類本身一同確立其在java虛擬機(jī)中的唯一性
-
雙親委派模型:java虛擬機(jī)存在兩種不同的類加載器,一是啟動(dòng)類加載器(Bootstrap ClassLoader),這個(gè)類加載器使用C++語(yǔ)言實(shí)現(xiàn),是虛擬機(jī)的一部分。另一種就是其他類加載器,由java語(yǔ)言實(shí)現(xiàn),獨(dú)立于虛擬機(jī)外部,并且全部繼承自抽象類java.lang.ClassLoader
image.png -
破壞雙親委派模型
- 第一次被破壞發(fā)生在雙親委派模型出現(xiàn)之前(JDK1.2發(fā)布之前)
- JDK1.2之后java.lang.ClassLoader添加了一個(gè)新的protected方法,findClass()
- 第二次破壞是由這個(gè)模型自身的缺陷所導(dǎo)致的,基礎(chǔ)類如果又要調(diào)回用戶的代碼會(huì)出問(wèn)題,為了解決這個(gè)問(wèn)題,java設(shè)計(jì)團(tuán)隊(duì)引入線程上下文加載器(Thread ClassLoader)。這個(gè)類加載器可以通過(guò)java.lang.Thread類的setContextClassLoader()方法設(shè)置,如果創(chuàng)建線程時(shí)沒(méi)設(shè)置,它將會(huì)從父線程中繼承一個(gè);如果在應(yīng)用程序的全局范圍內(nèi)都沒(méi)設(shè)置過(guò),那么這個(gè)類加載器默認(rèn)就是應(yīng)用程序類加載器
- 第三次破壞是由于用戶對(duì)程序動(dòng)態(tài)性的追求而導(dǎo)致的。在OSGi環(huán)境下,類加載器不再是雙親委派模型中的樹(shù)狀結(jié)構(gòu),而是進(jìn)一步發(fā)展為網(wǎng)狀結(jié)構(gòu)。
- 第一次被破壞發(fā)生在雙親委派模型出現(xiàn)之前(JDK1.2發(fā)布之前)
八、JVM GC機(jī)制
Java GC(Garbage Collection,垃圾收集,垃圾回收)機(jī)制
-
JVM通過(guò)GC來(lái)回收堆和方法區(qū)中的內(nèi)存,這個(gè)過(guò)程是自動(dòng)執(zhí)行的。說(shuō)到Java GC機(jī)制,其主要完成3件事:
- 確定哪些內(nèi)存需要回收;
- 確定什么時(shí)候需要執(zhí)行GC;
- 如何執(zhí)行GC。
-
垃圾檢測(cè)、回收算法:垃圾收集器一般必須完成兩件事:檢測(cè)出垃圾;回收垃圾。怎么檢測(cè)出垃圾?一般有以下幾種方法
- 引用計(jì)數(shù)法:給一個(gè)對(duì)象添加引用計(jì)數(shù)器,每當(dāng)有個(gè)地方引用它,計(jì)數(shù)器就加1;引用失效就減1。如果我有兩個(gè)對(duì)象A和B,互相引用,除此之外,沒(méi)有其他任何對(duì)象引用它們,實(shí)際上這兩個(gè)對(duì)象已經(jīng)無(wú)法訪問(wèn),即是我們說(shuō)的垃圾對(duì)象。但是互相引用,計(jì)數(shù)不為0,導(dǎo)致無(wú)法回收。
- 根搜索算法(GC Roots Tracing)
這個(gè)算法的基本思路就是通過(guò)一系列的名為“GC Roots”的對(duì)象做起點(diǎn),從這些節(jié)點(diǎn)開(kāi)始向下搜索,搜索所走過(guò)的路徑成為引用鏈(Reference Chain),當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈相連時(shí),則證明此對(duì)象是不可用的。
-
在Java語(yǔ)言里,可作為GC Roots的對(duì)象包括下面幾種:
- 虛擬機(jī)棧(棧幀中的本地變量表)中的引用對(duì)象
- 方法區(qū)中的類靜態(tài)屬性引用的對(duì)象
- 方法區(qū)中的常量引用對(duì)象
- 本地方法棧中JNI(即一般說(shuō)的Native方法)的引用對(duì)象
-
總之,JVM在做垃圾回收的時(shí)候,會(huì)檢查堆中的所有對(duì)象是否會(huì)被這些根集對(duì)象引用,不能夠被引用的對(duì)象就會(huì)被垃圾收集器回收。一般回收算法也有如下幾種:
- 標(biāo)記-清除算法(Mark-sweep)
- 算法和名字一樣,分為兩個(gè)階段:標(biāo)記和清除。標(biāo)記所有需要回收的對(duì)象,然后統(tǒng)一回收。這是最基礎(chǔ)的算法,后續(xù)的收集算法都是基于這個(gè)算法擴(kuò)展的。
- 不足:效率低;標(biāo)記清除之后會(huì)產(chǎn)生大量碎片。
- 復(fù)制算法(Copying)
- 此算法把內(nèi)存空間劃為兩個(gè)相等的區(qū)域,每次只使用其中一個(gè)區(qū)域。垃圾回收時(shí),遍歷當(dāng)前使用區(qū)域,把正在使用中的對(duì)象復(fù)制到另外一個(gè)區(qū)域中。
- 此算法每次只處理正在使用中的對(duì)象,因此復(fù)制成本比較小,同時(shí)復(fù)制過(guò)去以后還能進(jìn)行相應(yīng)的內(nèi)存整理,不會(huì)出現(xiàn)“碎片”問(wèn)題。當(dāng)然,此算法的缺點(diǎn)也是很明顯的,就是需要兩倍內(nèi)存空間。
- 標(biāo)記-整理算法(Mark-Compact)
- 此算法結(jié)合了“標(biāo)記-清除”和“復(fù)制”兩個(gè)算法的優(yōu)點(diǎn)。也是分兩階段,第一階段從根節(jié)點(diǎn)開(kāi)始標(biāo)記所有被引用對(duì)象,第二階段遍歷整個(gè)堆,把清除未標(biāo)記對(duì)象并且把存活對(duì)象“壓縮”到堆的其中一塊,按順序排放。
- 此算法避免了“標(biāo)記-清除”的碎片問(wèn)題,同時(shí)也避免了“復(fù)制”算法的空間問(wèn)題。
- 分代收集算法(Generational Collection)
- 這是當(dāng)前商業(yè)虛擬機(jī)常用的垃圾收集算法。分代的垃圾回收策略,是基于這樣一個(gè)事實(shí):不同的對(duì)象的生命周期是不一樣的。因此,不同生命周期的對(duì)象可以采取不同的收集方式,以便提高回收效率。
- 標(biāo)記-清除算法(Mark-sweep)
- 垃圾收集器
九、JVM性能監(jiān)控工具
- Jps(JVM Process Status Tools)
- Jps是參照Unix系統(tǒng)的取名規(guī)則命名的,而他的功能和ps的功能類似,可以列舉正在運(yùn)行的餓虛擬機(jī)進(jìn)程并顯示虛擬機(jī)執(zhí)行的主類以及這些進(jìn)程的唯一ID(LVMID,對(duì)應(yīng)本機(jī)來(lái)說(shuō)和PID相同)
- jstat(JVM Statistics Monitoring Tools)
- Jstat主要用于監(jiān)控虛擬機(jī)的各種運(yùn)行狀態(tài)信息,如類的裝載、內(nèi)存、垃圾回收、JIT編譯器等,在沒(méi)有GUI的服務(wù)器上,這款工具是首選的一款監(jiān)控工具
- jinfo(JVM configuration Info for Java)
- Jinfo的作用是實(shí)時(shí)查看虛擬機(jī)的各項(xiàng)參數(shù)信息jps –v可以查看虛擬機(jī)在啟動(dòng)時(shí)被顯式指定的參數(shù)信息
- jmap(JVM Memory Map for Java)
- Jmap用于生成堆快照(heapdump)
- jhat(JVM Heap Analysis Tool)
- Jhat是用來(lái)分析dump文件的一個(gè)微型的HTTP/HTML服務(wù)器,它能將生成的dump文件生成在線的HTML文件,讓我們可以通過(guò)瀏覽器進(jìn)行查閱,然而實(shí)際中我們很少使用這個(gè)工具,因?yàn)橐话惴?wù)器上設(shè)置的堆、棧內(nèi)存都比較大,生成的dump也比較大,直接用jhat容易造成內(nèi)存溢出,而是我們大部分會(huì)將對(duì)應(yīng)的文件拷貝下來(lái),通過(guò)其他可視化的工具進(jìn)行分析。
- jstack(JVM Stack Trace for java)
- Jstack用于JVM當(dāng)前時(shí)刻的線程快照,又稱threaddump文件,它是JVM當(dāng)前每一條線程正在執(zhí)行的堆棧信息的集合。生成線程快照的主要目的是為了定位線程出現(xiàn)長(zhǎng)時(shí)間停頓的原因,如線程死鎖、死循環(huán)、請(qǐng)求外部時(shí)長(zhǎng)過(guò)長(zhǎng)導(dǎo)致線程停頓的原因。通過(guò)jstack我們就可以知道哪些進(jìn)程在后臺(tái)做些什么?在等待什么資源等!
- JConsole(JVM Monitoring and management console)(可視化)
- 執(zhí)行JConsole即可查看效果
- VisualVM被成為是more in one的工具集(可視化)
- 執(zhí)行jvisualvm即可查看效果