一颜启、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
-
破壞雙親委派模型
- 第一次被破壞發(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即可查看效果