《深入理解JAVA虚拟机》——学习笔记
JVM內(nèi)存模型以及分區(qū)
JVM內(nèi)存分為:
1.方法區(qū):線程共享的區(qū)域,存儲已經(jīng)被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)
2.堆:線程共享的區(qū)域,存儲對象實(shí)例,以及給數(shù)組分配的內(nèi)存區(qū)域也在這里。
3.虛擬機(jī)棧:線程隔離的區(qū)域,每個(gè)線程都有自己的虛擬機(jī)棧,生命周期和線程相同。虛擬機(jī)棧描述方法執(zhí)行的內(nèi)存模型,以站棧幀為單位,每個(gè)棧幀存儲和方法運(yùn)行有關(guān)的局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法返回地址等信息。
4.程序計(jì)數(shù)器:線程隔離的區(qū)域,每個(gè)線程都有自己的程序計(jì)數(shù)器,存儲程序當(dāng)前執(zhí)行的字節(jié)碼的行號。
5.本地方法棧:線程隔離,和虛擬機(jī)棧類似,是虛擬機(jī)調(diào)用Native方法時(shí)使用的。
?
堆的分區(qū),以及各個(gè)分區(qū)的特點(diǎn):
Java堆是垃圾收集器管理的主要區(qū)域,按照分代收集算法的劃分,堆內(nèi)存空間可以繼續(xù)細(xì)分為年輕代,老年代。年輕代又可以劃分為較大的Eden區(qū),兩個(gè)同等大小的From Survivor,To Survivor區(qū)。默認(rèn)的Eden區(qū)和Survivor區(qū)的大小比例為8:1:1,這個(gè)比例可以調(diào)節(jié)。在為新創(chuàng)建的對象分配內(nèi)存的時(shí)候先將對象分配到Eden區(qū)和From Survivor區(qū),在立即回收時(shí),會(huì)將Eden區(qū)和Survivor區(qū)還存活的對象復(fù)制到To Survivor區(qū)中,如果To Survivor區(qū)的大小不能容納存活的對象,會(huì)把存活的對象分配到老年區(qū)。總體來說,新創(chuàng)建的小對象會(huì)放在年輕代,年輕代的對象大多在下一次垃圾回收時(shí)被回收,老年代存儲大的對象和存活時(shí)間長的對象。
?
對象的創(chuàng)建方法,對象的內(nèi)存布局,對象的訪問定位
對象的創(chuàng)建:
1.普通對象的創(chuàng)建過程:虛擬機(jī)遇到一條new指令時(shí),首先檢查這個(gè)指令的參數(shù)(類的類型)是否能在常量池中定位到一個(gè)類的符號引用,并且檢查這個(gè)符號引用代表的類時(shí)候已經(jīng)被加載、解析、初始化過,如果沒有要執(zhí)行類加載過程。
2.數(shù)組對象的創(chuàng)建:虛擬機(jī)遇到一條newarray字節(jié)碼指令會(huì)在內(nèi)存中直接分配一塊區(qū)域。
3.Class對象的創(chuàng)建:在虛擬機(jī)加載類的時(shí)候,通過類的全限定名獲取此類的二進(jìn)制字節(jié)流,再通過文件驗(yàn)證后把字節(jié)流代表的靜態(tài)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),并且在內(nèi)存中生成一個(gè)代表這個(gè)類的Class對象,存在方法區(qū)中,作為這個(gè)類的各種數(shù)據(jù)的訪問入口。
?
對象的內(nèi)存布局:
對象在內(nèi)存中的布局分為三塊區(qū)域:對象頭、實(shí)例數(shù)據(jù)、對齊填充
對象頭:存儲對象自身的運(yùn)行時(shí)數(shù)據(jù),包括哈希嗎,GC分代年齡,鎖狀態(tài)標(biāo)識,線程持有的鎖,偏向線程ID,偏向時(shí)間戳等;對象頭的另外一部分是類型指針,即對象指向它在方法區(qū)中的類元數(shù)據(jù)的指針,虛擬機(jī)通過這個(gè)指針來確定該對象是哪個(gè)類的實(shí)例。如果對象是一個(gè)數(shù)組,對象頭還有一塊用語記錄數(shù)組長度的數(shù)據(jù)。
實(shí)例數(shù)據(jù):對象真正存儲的有效信息,是在類中定義的各種類型的字段內(nèi)容。
對齊填充:虛擬機(jī)要求對象的大小必須是8字節(jié)的整數(shù)倍,對齊填充起占位符的作用,保證對象大小為8字節(jié)的整數(shù)倍。
?
對象的訪問定位:Java程序通過棧上的引用數(shù)據(jù)操作堆中的具體對象,對象訪問方式有兩種:句柄訪問,直接指針訪問。
句柄訪問:Java堆劃分出一塊區(qū)域用作句柄池,引用中存儲對象的句柄地址,句柄中才實(shí)際包含著對象實(shí)例數(shù)據(jù)和對象類型數(shù)據(jù)各自的具體地址信息。
直接指針訪問:棧中的引用直接指向?qū)ο笤诙阎械牡刂?#xff0c;對象在頭數(shù)據(jù)中指向方法區(qū)中其類元數(shù)據(jù)的地址。
使用句柄的好處是引用中存儲的是穩(wěn)定的句柄地址,在對象被移動(dòng)(垃圾回收導(dǎo)致對象的移動(dòng))時(shí)只會(huì)改變局并重的實(shí)例數(shù)據(jù)指針。使用直接指針訪問的好處是速度更快。
?
垃圾回收的判定方法:引用計(jì)數(shù)法,引用鏈法
引用計(jì)數(shù)法:給對象添加一個(gè)引用計(jì)數(shù)器,有對象引用計(jì)數(shù)器加1,引用失效計(jì)數(shù)器減1,計(jì)數(shù)器為0表示對象不再被使用,可以被回收。
引用鏈法(可達(dá)性分析):通過GC Roots作為起點(diǎn),當(dāng)一個(gè)對象到到GC Roots沒有任何引用鏈相連時(shí),證明對象時(shí)不可用的。
可作為GC Roots的對象是虛擬機(jī)棧中引用的對象、本地方法棧中引用的對象、方法區(qū)中類靜態(tài)屬性引用的對象,方法區(qū)中常量引用的對象(執(zhí)行上下文和全局性引用)
?
Java的四種引用類型及特點(diǎn):
1.強(qiáng)引用:程序中普遍存在的,類似“String s=”hello wold””這類的引用,強(qiáng)引用的對象不會(huì)被回收。
2.軟引用:有用但是非必須的對象在系統(tǒng)將要發(fā)生內(nèi)存溢出之前會(huì)對軟引用的對象進(jìn)行垃圾回收,SoftReference類實(shí)現(xiàn)軟引用。
3.弱引用:非必須的對象,被弱引用關(guān)聯(lián)的對象只能存活到下一次垃圾收集發(fā)生之前。
4.虛引用:最弱的引用關(guān)系,不能通過虛引用取得對象的實(shí)例,為對象設(shè)置虛引用的唯一目的就是在這個(gè)對象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知。
四種引用強(qiáng)度依次減弱,強(qiáng)軟弱虛。
?
GC的三種收集算法的原理和特點(diǎn),用途,優(yōu)化思路
三種垃圾收集算法:復(fù)制算法,標(biāo)記-清除算法、標(biāo)記-整理算法
標(biāo)記-清除算法:首先標(biāo)記出所有需要回收的對象,標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象。缺點(diǎn):標(biāo)記和清除兩個(gè)過程效率都不高;標(biāo)記清楚后會(huì)產(chǎn)生空間碎片,空間碎片導(dǎo)致分配較大對象時(shí)可能提前出發(fā)垃圾回收。
復(fù)制算法:將可用內(nèi)存分為兩個(gè)區(qū)域,每次只使用其中一塊,當(dāng)使用的那一塊內(nèi)存用完時(shí),將還存活的對象復(fù)制到另外一塊內(nèi)存中,然后把已使用過的內(nèi)存空間一次清理掉。優(yōu)點(diǎn):解決的空間碎片問題,實(shí)現(xiàn)簡單。缺點(diǎn):將內(nèi)存縮小為兩塊,內(nèi)存使用率不高。復(fù)制操作頻繁效率變低。
標(biāo)記-整理算法:可回收對象標(biāo)記后,讓所有存活的對象向一端移動(dòng),然后清理掉邊界以外的內(nèi)存。優(yōu)點(diǎn):不會(huì)產(chǎn)生空間碎片,比復(fù)制算法提高了內(nèi)存空間利用率。
復(fù)制算法用在年輕代的垃圾回收中,標(biāo)記整理和標(biāo)記清除算法用在老年代垃圾回收的收集器中。
?
GC收集器有哪些?CMS和G1收集器的特點(diǎn)
GC收集器按照回收區(qū)域不同,新生代有Serial,Parnew,Paralell Scanvage,老年代有Serial Old,CMS,Parallel old,還有新生代老年代通用的G1;
Serial 和Serial old是早期jdk中發(fā)布的垃圾收集器,特點(diǎn)是都為單線程,新生代采用復(fù)制算法,老年代采用標(biāo)記整理算法,兩個(gè)垃圾收集器在工作的時(shí)候必須要停掉所有的用戶線程,直到收集完成后才能回復(fù)用戶線程,由于是單線程工作方式,沒有線程交互的開銷所以能夠活的最高的單線程收集效率,使用在client模式下的虛擬機(jī)。
ParNew收集器是Serial收集器的多線程版本,是年輕代的垃圾收集器,可以和Serial old以及CMS老年代收集器搭配使用。Parnew在單CPU環(huán)境中的性能沒有Serial好,因?yàn)閱蜟PU環(huán)境下的多線程按照時(shí)間順序串行執(zhí)行,還要承擔(dān)線程間交互的額外開銷,不過在多cpu環(huán)境下,Parnew的性能就會(huì)好很多,是運(yùn)行在server模式下的虛擬機(jī)首選的新生代收集器。
在jdk1.4時(shí)新推出的垃圾收集器是Parallel Scanvage 和對應(yīng)的Parallel Old,新生代基于復(fù)制算法,老年代基于標(biāo)記整理算法.Parallel Scanvage也是并行性的多線程收集器,它和Parnew 的區(qū)別在于兩者的關(guān)注點(diǎn)不同。Parnew關(guān)注于減少垃圾回收時(shí)用戶線程停頓的時(shí)間,而Parllel Scanvage 關(guān)注點(diǎn)事獲得最大的吞吐量,也就是CPU運(yùn)行用戶代碼與CPU總消耗時(shí)間的比值。停頓時(shí)間短適合于和用戶有交互的程序,吞吐量高則可以高效的利用CPU時(shí)間,盡快完成運(yùn)算任務(wù),主要是和在后臺運(yùn)算不需要太多的交互任務(wù)。
Jdk1.5時(shí)推出了能夠和用戶線程并發(fā)執(zhí)行的CMS收集器,CMS是老年代垃圾收集器。CMS是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器,基于標(biāo)記清除算法來實(shí)現(xiàn)。它的工作過程先后分為初始標(biāo)記、并發(fā)標(biāo)記、重新標(biāo)記、并發(fā)清除四個(gè)步驟,其中初始標(biāo)記和重新標(biāo)記是需要停頓用戶線程的,并發(fā)標(biāo)記和并發(fā)清理過程是可以和用戶線程并發(fā)執(zhí)行的,在整體垃圾收集時(shí)間里,初始標(biāo)記和重新標(biāo)記所占的時(shí)間很少,重新標(biāo)記階段又是可以多個(gè)垃圾回收線程并行執(zhí)行的,所以整體用戶線程停頓的時(shí)間很短。CMS的缺點(diǎn):對CPU資源敏感,CMS默認(rèn)啟動(dòng)的垃圾回收線程數(shù)為(CPU數(shù)量+3)/4,在并發(fā)階段由于占用用戶線程導(dǎo)致應(yīng)用變慢,cpu不足4個(gè)時(shí)候?qū)τ脩舫绦蛴绊懞艽?#xff1b;CMS無法處理在并發(fā)清理階段新產(chǎn)生的垃圾,只有等下一次垃圾回收標(biāo)記后才能清除;CMS基于標(biāo)記清除算法會(huì)產(chǎn)生空間碎片,CMS的解決方式是在進(jìn)行Full GC時(shí)開啟內(nèi)存整理,這一過程無法并發(fā),延長了用戶線程的停頓時(shí)間。
G1收集器是在jdk1.7時(shí)推出的用語新生代和來年代的垃圾收集器,面向server模式。G1把內(nèi)存區(qū)域劃分成多個(gè)大小相同的獨(dú)立區(qū)域,G1跟蹤每個(gè)區(qū)域里面垃圾堆積的價(jià)值大小,在后臺維護(hù)一個(gè)優(yōu)先列表,每次根據(jù)允許的收集時(shí)間,優(yōu)先回收價(jià)值最大的區(qū)域,這種收集策略可以在有限時(shí)間內(nèi)獲取盡可能高的收集效率。G1垃圾回收過程:初始標(biāo)記(單線程,停頓)、并發(fā)標(biāo)記(單線程,并發(fā))、最終標(biāo)記(多線程,并行,停頓)、篩選回收(多線程,并行,停頓)。
?
Minor GC和Full GC分別發(fā)生在什么時(shí)候?
當(dāng)創(chuàng)建對象分配的內(nèi)存空間不足時(shí)會(huì)啟動(dòng)一次Minor GC,收集新生代的Eden區(qū)和From Survivor區(qū),把還存活的對象分配到To Survivor區(qū),如果To Survivor區(qū)的空間不足以容納存活的對象,會(huì)把存活的對象分配到老年代,如果老年代也沒有足夠的空間會(huì)啟動(dòng)一次Full GC。
?
類加載過程:加載、驗(yàn)證、準(zhǔn)備、解析、初始化
虛擬機(jī)的類加載機(jī)制就是把描述類的數(shù)據(jù)從Class文件(或者其他途徑)加載到內(nèi)存,并對數(shù)據(jù)進(jìn)行校驗(yàn)、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的Java類型。
加載:1.通過一個(gè)類的全限定名獲取定義此類的二進(jìn)制字節(jié)流2、將這個(gè)字節(jié)流所戴曉的靜態(tài)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)3、在內(nèi)存中生成一個(gè)代表這個(gè)類的Class對象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口。
驗(yàn)證:1、文件格式驗(yàn)證,保證輸入的字節(jié)流在格式上符合Class文件的格式規(guī)范,保證輸入的字節(jié)流能正確的解析,只有通過這個(gè)驗(yàn)證,字節(jié)流才會(huì)存儲在方法區(qū)之內(nèi)2、元數(shù)據(jù)驗(yàn)證,對類的元數(shù)據(jù)進(jìn)行語義校驗(yàn),保證類描述的信息符合Java語言規(guī)范。比如驗(yàn)證類的是否實(shí)現(xiàn)了父類或者接口中的方法等3、字節(jié)碼驗(yàn)證,通過數(shù)據(jù)流和控制流的分析,確保類的方法符合邏輯,不會(huì)在運(yùn)行時(shí)對虛擬機(jī)產(chǎn)生危害4、符號引用校驗(yàn),發(fā)生在解析階段,確保解析階段將符號引用轉(zhuǎn)化為直接飲用的正常執(zhí)行。
準(zhǔn)備:正式為類變量(static)分配內(nèi)存,并設(shè)置類變量初始值(數(shù)據(jù)類型的零值),這些變量所使用的內(nèi)存在方法區(qū)中分配。
解析:虛擬機(jī)將常量池內(nèi)的符號引用轉(zhuǎn)化為直接飲用,解析動(dòng)作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調(diào)用點(diǎn)限定符7類符號引用進(jìn)行。
初始化:初始化階段才真正執(zhí)行類中定義的Java代碼,初始化階段是執(zhí)行類構(gòu)造器<clinit>方法的過程。<clinit>方法(類構(gòu)造器)是由編譯器自動(dòng)收集類中的靜態(tài)變量和靜態(tài)代碼塊合并產(chǎn)生的。子類和父類的初始化過程優(yōu)先級為:父類類構(gòu)造器->子類類構(gòu)造器->父類對象構(gòu)造函數(shù)->子類對象構(gòu)造函數(shù)。類中靜態(tài)類變量和靜態(tài)代碼塊是按照在類中定義的順序執(zhí)行的。
?
什么時(shí)候進(jìn)行類的初始化?
JVM規(guī)定了有且僅有5中情況——對類進(jìn)行主動(dòng)引用,必須立即執(zhí)行類的初始化。
1)、遇到new,putstatic,getstatic,invokespecial四條字節(jié)碼指令的時(shí)候,如果沒有進(jìn)行類的初始化要立即初始化。這四條字節(jié)碼指令對應(yīng)的編程中的環(huán)境為:使用new關(guān)鍵字實(shí)例化對象,讀取或設(shè)置類的靜態(tài)變量,調(diào)用類的靜態(tài)方法。
2)、使用java.lang.reflect包對類進(jìn)行反射的時(shí)候,如果沒有初始化要立即初始化。
3)、初始化一個(gè)類的時(shí)候,如果其父類沒有進(jìn)行初始化要先出發(fā)父類的初始化
4)、虛擬機(jī)啟動(dòng)的時(shí)候,main方法所在的主類會(huì)被虛擬機(jī)先初始化
5)、使用動(dòng)態(tài)語言在lava.lang.invoke.MethodHandle實(shí)例最后的解析結(jié)果是REF_getdtatic,REF_putStatic,REF_invokeStatic的方法句柄,這個(gè)句柄對應(yīng)的類沒有被初始化需要先觸發(fā)其初始化。
?
雙親委派模型:
類加載器的雙親委派模型是指從頂層到底層分別是啟動(dòng)類加載器、擴(kuò)展類加載器、應(yīng)用程序類加載器、自定義類加載器。類加載器之間的父子關(guān)系不是通過繼承來實(shí)現(xiàn),而是通過組合來實(shí)現(xiàn)。雙親委派模型的工作過程是:如果一個(gè)類加載器收到了類加載的請求,首先把這個(gè)請求委派給父類加載器去完成,所有的加載請求最終都應(yīng)該傳送到頂層的啟動(dòng)類加載器中,只有當(dāng)父類反饋?zhàn)约簾o法完成類加載請求的時(shí)候,自加載器才會(huì)嘗試自己去加載。
使用雙親委派模型的好處:java類隨著他的加載器一起具備了帶有優(yōu)先級的層次結(jié)構(gòu),最基礎(chǔ)的類由頂層的類加載器加載,這樣保證在程序中使用該類的地方使用的都是這同一個(gè)類。
?
轉(zhuǎn)載于:https://www.cnblogs.com/gl-developer/p/6502600.html
總結(jié)
以上是生活随笔為你收集整理的《深入理解JAVA虚拟机》——学习笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 201521123030《Java程序设
- 下一篇: 解决nginx重启“var/run/ng