JVM原理入門
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
76套java從入門到精通實(shí)戰(zhàn)課程分享

類裝載器ClassLoader
負(fù)責(zé)加載class文件,class文件在文件開頭有特定的文件標(biāo)識(shí),將class文件字節(jié)碼內(nèi)容加載到內(nèi)存中,并將這些內(nèi)容轉(zhuǎn)換成方法區(qū)中的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)并且ClassLoader只負(fù)責(zé)class文件的加載,至于它是否運(yùn)行,則是由Execution Engine決定 (快遞員)
1、虛擬機(jī)自帶的加載器:
啟動(dòng)類加載器(Bootstrap)C++ :加載jdk原始自帶的類,在jdk1.8.0_201\jre\lib\rt.jar下,如Object、String、ArrayList等 但是如果要用getClassLoader獲取加載器名的話會(huì)顯示null,因?yàn)樵摷虞d器是C++寫的不是Java的
擴(kuò)展類加載器(Extension)Java:加載jdk升級過程中擴(kuò)展的類,在jdk1.8.0_201\jre\lib\ext目錄下
應(yīng)用程序類加載器(AppClassLoader)java也叫系統(tǒng)類加載器,加載當(dāng)前 應(yīng)用的classpath的所有類


2、用戶自定義加載器
Java.lang.ClassLoader的子類,用戶可以定制的加載方式
3、常見面試問題
雙親委派機(jī)制:
(1)如果一個(gè)類加載器收到了類加載請求,不自己加載而是把請求給其父類去執(zhí)行.
(2)若父類加載器還存在其父類的加載器,進(jìn)一步向上委托.
(3)如果父類加載器可以完成類加載任務(wù)就返回,若不能才讓自己子類加載
沙箱安全:雙親委派機(jī)制保證了Java的出廠源碼不會(huì)受到開發(fā)人員編寫的污染(沙箱安全機(jī)制)
本地方法接口
一個(gè)Native Method就是一個(gè)Java調(diào)用非Java代碼的接口,該方法是由非Java語言實(shí)現(xiàn)。
有時(shí)Java應(yīng)用需要與Java外面的環(huán)境交互,這是本地方法存在的主要原因。你可以想想Java需要與一些底層系統(tǒng),如操作系統(tǒng)或某些硬件交換信息時(shí)的情況。本地方法正是這樣一種交流機(jī)制:它為我們提供了一個(gè)非常簡潔的接口,而且我們無需去了解Java應(yīng)用之外的繁瑣的細(xì)節(jié)。
PC寄存器(程序計(jì)數(shù)器)
每一個(gè)線程都有一個(gè)程序計(jì)數(shù)器,是線程私有的,就是一個(gè)指針,用來存儲(chǔ)指向下一條指令的地址,也就是將要執(zhí)行的指令代碼。是一塊兒非常小的內(nèi)存,它是當(dāng)前線程所要執(zhí)行的字節(jié)碼的行號指示器。如果執(zhí)行的是一個(gè)Native方法,那么這個(gè)計(jì)數(shù)器是空的。
方法區(qū)
它存儲(chǔ)了每個(gè)類的結(jié)構(gòu)信息,例如運(yùn)行時(shí)常量池、字段和方法數(shù)據(jù)、構(gòu)造函數(shù)和普通方法的字節(jié)碼內(nèi)容。
上面說的是規(guī)范,在不同的虛擬機(jī)里實(shí)現(xiàn)是不一樣的。最經(jīng)典的就是永久代和元空間。
實(shí)例變量存在堆內(nèi)存中,與方法區(qū)無關(guān)
stack(java棧)
棧又叫棧內(nèi)存,主管java程序的運(yùn)行,是在線程創(chuàng)建時(shí)創(chuàng)建,他的生命周期是跟隨線程的生命周期,線程結(jié)束棧內(nèi)存也就釋放,對于棧來說不存在垃圾回收問題,生命周期和線程一致,是線程私有的。8種基本類型的變量+對象的引用變量+實(shí)例方法都是在函數(shù)的棧內(nèi)存中分配的。
5.1棧幀(在外面就是方法)中主要保存3類數(shù)據(jù):
本地變量:輸入?yún)?shù)和輸出參數(shù)以及方法內(nèi)的變量;
棧操作:記錄出棧、入棧的操作;
棧幀數(shù)據(jù):包括類文件、方法等等。

5.2java.lang.StackOverflowError (棧溢出)他是一個(gè)錯(cuò)誤,是一個(gè)error
5.3 棧+堆+方法區(qū)的交互關(guān)系
HotSpot(一般咱們的jdk都是這個(gè)版本)是使用指針的方式來訪問對象。
Java堆中會(huì)存放訪問類元數(shù)據(jù)的地址
reference存儲(chǔ)的就直接是對象的地址

heap堆
一個(gè)JVM實(shí)例只存在一個(gè)堆內(nèi)存,堆內(nèi)存的大小是可以調(diào)節(jié)的。
先看下JAVA堆內(nèi)存是如何劃分的,如圖:

(1)JVM內(nèi)存簡單可以劃分為堆內(nèi)存和非堆內(nèi)存,堆內(nèi)存分為年輕代(Young Generation)、老年代(Old Generation),非堆內(nèi)存有永久代(Permanent Generation),jdk1.8及之后元空間取代永久代。
(2)年輕代又分為Eden和Survivor區(qū)。Survivor區(qū)由FromSpace和ToSpace組成。Eden區(qū)占大容量,Survivor兩個(gè)區(qū)占小容量,默認(rèn)比例是8:1:1。
(3)堆內(nèi)存用途:存放的是對象,垃圾收集器就是收集這些對象,然后根據(jù)GC算法回收。
(4)非堆內(nèi)存用途:永久代,一般hotspot下也稱為方法區(qū),存儲(chǔ)程序運(yùn)行時(shí)長期存活的對象,比如類的元數(shù)據(jù)、方法、常量、屬性等。
重點(diǎn):
(1)首先當(dāng)Eden區(qū)滿的時(shí)候會(huì)觸發(fā)第一次GC,把活著的對象拷貝到SurvivorFrom區(qū)
然后當(dāng)Eden區(qū)再次觸發(fā)GC的時(shí)候會(huì)掃描Eden+From區(qū),對這兩個(gè)區(qū)域進(jìn)行垃圾回收,經(jīng)過這次回收后還存活的對象,則直接復(fù)制到To區(qū)(如果有對象的年齡已經(jīng)達(dá)到了老年的標(biāo)準(zhǔn),則賦值到老年代區(qū)),同時(shí)把這些對象的年齡+1
(2)清空Eden、SurvivorFrom
然后,清空Eden和SurvivorFrom中的對象,也即復(fù)制之后有交換,誰空誰是to
Old(養(yǎng)老區(qū))滿了,開啟
Full GC = FGC (重GC)
如果Full GC多次,發(fā)現(xiàn)老年代也滿了,就會(huì)報(bào) OOM(堆內(nèi)存溢出)

元空間與永久代
Jdk1.8以后開始把類的元數(shù)據(jù)放在元空間(Metaspace)中,該區(qū)域在jdk7及以前是屬于永久帶的,元空間和永久代都是用來存儲(chǔ)class相關(guān)信息,包括class對象的Method,F(xiàn)ield等,元空間和永久代其實(shí)都是方法區(qū)的實(shí)現(xiàn),只是實(shí)現(xiàn)有所不同,所以說方法區(qū)其實(shí)只是一種JVM的規(guī)范。
兩者最大的區(qū)別是元空間使用本地內(nèi)存,而永久代使用的是JVM的內(nèi)存,也就是說,本地內(nèi)存剩余多少理論上metaspace就可以有多大,這解決了空間不足的問題,不過也不可能任其無限壯大,JVM默認(rèn)在運(yùn)行時(shí)會(huì)根據(jù)需要?jiǎng)討B(tài)的設(shè)置其大小。
GC是什么(分代收集算法)
1、次數(shù)上頻繁收集Young區(qū)
2、次數(shù)上較少收集Old區(qū)
3、基本不動(dòng)Perm區(qū)
JVM在進(jìn)行GC時(shí),大部分時(shí)候回收的都是新生代。
因此GC按照回收的區(qū)域分了兩種類型,一種是普通的GC(minor GC),一種是全局GC(minor GC or Full GC)
針對HotSpot VM的實(shí)現(xiàn),它里面的GC其實(shí)準(zhǔn)確分類只有兩大種:
1.Partial GC:并不收集整個(gè)GC堆的模式,具體如下:
Young GC/Minor GC:只收集新生代的GC。
Old GC:只收集老年代的GC。只有CMS的concurrent collection是這個(gè)模式。
Mixed GC:收集整個(gè)新生代以及部分老年代的GC,只有G1有這個(gè)模式。
2.Full GC/Major GC:收集整個(gè)GC堆的模式,包括新生代、老年代、永久代(如果存在的話)等所有部分的模式。
常見垃圾收集算法
標(biāo)記 - 清除算法
首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象。它的主要不足有兩個(gè):一個(gè)是效率問題,標(biāo)記和清除兩個(gè)過程的效率都不高;另一個(gè)是空間問題,標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會(huì)導(dǎo)致以后在程序運(yùn)行過程中需要分配較大對象時(shí),無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作。
復(fù)制算法
為了解決效率問題,一種稱為“復(fù)制”(Copying)的收集算法出現(xiàn)了,它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對象復(fù)制到另外一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉。這樣使得每次都是對整個(gè)半?yún)^(qū)進(jìn)行內(nèi)存回收,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況,只要移動(dòng)堆頂指針,按順序分配內(nèi)存即可,實(shí)現(xiàn)簡單,運(yùn)行高效。只是這種算法的代價(jià)是將內(nèi)存縮小為了原來的一半,未免太高了一點(diǎn)。
標(biāo)記 - 整理算法
復(fù)制收集算法在對象存活率較高時(shí)就要進(jìn)行較多的復(fù)制操作,效率將會(huì)變低。更關(guān)鍵的是,如果不想浪費(fèi)50%的空間,就需要有額外的空間進(jìn)行分配擔(dān)保,以應(yīng)對被使用的內(nèi)存中所有對象都100%存活的極端情況,所以在老年代一般不能直接選用這種算法。
根據(jù)老年代的特點(diǎn),有人提出了另外一種“標(biāo)記-整理”(Mark-Compact)算法,標(biāo)記過程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對可回收對象進(jìn)行清理,而是讓所有存活的對象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存
————————————————
版權(quán)聲明:本文為CSDN博主「coderzpw」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:
https://blog.csdn.net/qq_45464560/article/details/115302171
粉絲福利:Java從入門到入土學(xué)習(xí)路線圖
??????

??長按上方微信二維碼 2 秒
感謝點(diǎn)贊支持下哈 
