摘要:于是這篇博客就針對(duì)虛擬機(jī)的各個(gè)知識(shí)點(diǎn)進(jìn)行歸納。若虛擬機(jī)棧請(qǐng)求擴(kuò)展時(shí)無法申請(qǐng)到足夠的內(nèi)存,則拋出異常。類索引用于確定類的全限定名,父類索引用于確定父類的全限定名。字節(jié)碼指令操作碼長(zhǎng)度為一個(gè)字節(jié),所以總數(shù)最多不超過條。
Java虛擬機(jī)一直是Java的重難點(diǎn),一方面由于系統(tǒng)封裝得太好,你平常寫程序的時(shí)候幾乎感覺不到它的存在,另一方面了解必要的Java虛擬機(jī)工作原理才能對(duì)真實(shí)工作環(huán)境下的bug進(jìn)行對(duì)癥下藥,另外虛擬機(jī)這一部分也一直是面試考官愛問的問題。于是這篇博客就針對(duì)Java虛擬機(jī)的各個(gè)知識(shí)點(diǎn)進(jìn)行歸納。
一.Java內(nèi)存區(qū)域 運(yùn)行時(shí)數(shù)據(jù)區(qū)域 程序計(jì)數(shù)器程序計(jì)數(shù)器是當(dāng)前線程執(zhí)行的字節(jié)碼的行號(hào)指示器,線程私有,獨(dú)立存儲(chǔ)
Java虛擬機(jī)棧Java虛擬機(jī)棧是線程私有,與Java的方法執(zhí)行模型有關(guān),描述Java方法執(zhí)行的內(nèi)存模型:方法執(zhí)行時(shí)創(chuàng)建棧幀用于儲(chǔ)存局部變量表等信息,方法調(diào)用返回對(duì)應(yīng)棧幀再虛擬機(jī)中的入棧出棧。
既然是棧那么深度就是一定的,若線程請(qǐng)求棧深度大于虛擬機(jī)所規(guī)定的深度,則拋出StackOverflowError異常。若虛擬機(jī)棧請(qǐng)求擴(kuò)展時(shí)無法申請(qǐng)到足夠的內(nèi)存,則拋出OOM異常。
本地方法棧就是Native方法所用到的棧,與虛擬機(jī)棧作用類似。
Java堆Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,屬于線程共享區(qū),在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。它主要作用是存放對(duì)象實(shí)例和進(jìn)行垃圾收集管理。
方法區(qū)方法區(qū)也是各個(gè)線程共享的內(nèi)存區(qū)域,用于儲(chǔ)存已被虛擬機(jī)加載的類信息,常量,靜態(tài)變量,即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。
運(yùn)行時(shí)常量池運(yùn)行時(shí)常量池其實(shí)屬于方法區(qū),它主要用于存放編譯期生成的各種字面量和符號(hào)引用,并且具有動(dòng)態(tài)的特點(diǎn)。
new關(guān)鍵字的創(chuàng)建流程檢查指令的參數(shù)能否在常量池中定位到一個(gè)類的符號(hào)引用
檢查是否已經(jīng)加載解析和初始化
從Java堆中劃分內(nèi)存給新生對(duì)象,使用CAS保證分配的原子性
將內(nèi)存空間初始化為零值
對(duì)對(duì)象進(jìn)行設(shè)置,存放在對(duì)象頭中
執(zhí)行
指針碰撞
若Java堆中的內(nèi)存都是規(guī)整的,用過的內(nèi)存都在左邊,沒用過的都在右邊,中間指針指向臨界點(diǎn),分配內(nèi)存就很簡(jiǎn)單,只用把指針往右移動(dòng)和待分配對(duì)象一樣的內(nèi)存區(qū)域就行了。
空閑列表
如果內(nèi)存不是規(guī)整的,用過的和沒用過的內(nèi)存交錯(cuò)在一起,就不能使用指針碰撞了,需要維護(hù)一個(gè)列表記錄可用的內(nèi)存塊,分配內(nèi)存時(shí)就從列表中找一塊足夠大的內(nèi)存記錄下來。
對(duì)象的內(nèi)存布局 對(duì)象頭儲(chǔ)存對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù),eg:哈希碼,GC分代年齡,鎖狀態(tài)標(biāo)志等。還有類型指針指向它的類元數(shù)據(jù)的指針,通過這個(gè)指針確定這個(gè)對(duì)象是哪個(gè)類的實(shí)例。若是Java數(shù)組則對(duì)象頭還有一塊記錄數(shù)組長(zhǎng)度的數(shù)據(jù)。
實(shí)例數(shù)據(jù)程序代碼中所定義的各種類型的字段內(nèi)容,相同寬度的字段分配到一起
對(duì)象訪問定位虛擬機(jī)通過棧上的reference數(shù)據(jù)來操作堆上的具體對(duì)象。
訪問方式使用句柄
包含對(duì)象實(shí)例數(shù)據(jù)與類型數(shù)據(jù)各自的地址信息,reference中儲(chǔ)存的就是對(duì)象的句柄地址。句柄地址穩(wěn)定,對(duì)象移動(dòng)時(shí)只改變句柄中的實(shí)例數(shù)據(jù)指針,reference本身不修改。
直接指針
reference中儲(chǔ)存的就是對(duì)象地址,速度更快
二.垃圾收集器與內(nèi)存分配策略 引用計(jì)數(shù)算法給對(duì)象添加一個(gè)引用計(jì)數(shù)器,有一個(gè)地方引用它時(shí),計(jì)數(shù)器值就加一,引用失效時(shí)就減一,任何時(shí)刻計(jì)數(shù)值為0的對(duì)象就死了。這個(gè)算法雖然簡(jiǎn)單但是有一個(gè)致命的缺點(diǎn)就是無法解決對(duì)象之間相互循環(huán)引用的關(guān)系??蛇_(dá)性分析算法應(yīng)運(yùn)而生。
可達(dá)性分析算法GC Roots作為起點(diǎn)向下搜索,若一個(gè)對(duì)象到GC Roots沒有引用鏈的話,則證明此對(duì)象不可用,可以回收。搜索的對(duì)象有:
虛擬機(jī)棧中引用的對(duì)象
方法區(qū)中靜態(tài)屬性引用的對(duì)象
方法區(qū)中常量引用的對(duì)象
本地方法棧中Native 方法引用的對(duì)象
對(duì)象的回收經(jīng)歷對(duì)象在沒有引用鏈通往GC Roots時(shí),需要經(jīng)過兩次標(biāo)記才能真正死亡。
對(duì)象在進(jìn)行可達(dá)性分析后如果沒有與GC Roots相連接的引用鏈,會(huì)被第一次標(biāo)記并篩選,若對(duì)象沒有覆蓋finalize方法或者已經(jīng)調(diào)用過了則不會(huì)調(diào)用finalize。如果需要調(diào)用finalize方法,則對(duì)象被放在F-Queue隊(duì)列中,等待線程執(zhí)行。
對(duì)象如果想存活下去,finalize方法是最后的機(jī)會(huì),否則GC對(duì)F-Queue隊(duì)列進(jìn)行第二次標(biāo)記后對(duì)象真正死亡。
垃圾回收算法 標(biāo)記-消除算法首先標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收,缺點(diǎn)是效率低下而且產(chǎn)生大量的內(nèi)存碎片。
復(fù)制算法將內(nèi)存劃分為大小相等的兩塊,每次只使用其中的一塊,當(dāng)這一塊的內(nèi)存用完了,就將還存活的對(duì)象復(fù)制到另外一塊上面,然后把已經(jīng)使用的內(nèi)存空間一次清理掉。缺點(diǎn)是將內(nèi)存縮小為了原來的一半,代價(jià)較高,對(duì)象存活率較高時(shí)效率低。
HotSpot實(shí)際使用(回收新生代)則是將內(nèi)存劃分為較大的Eden區(qū)和兩塊較小的Survivor區(qū),一塊Eden區(qū)和一塊Survivor區(qū)大小比例為8:1,垃圾回收時(shí)就將Eden區(qū)和已使用的Survivor區(qū)中還存活的對(duì)象移到另一塊Survivor區(qū)中,由于根據(jù)統(tǒng)計(jì),98%的對(duì)象都是很快死亡的,所以按照8:1:1的比例來劃分內(nèi)存明顯比1:1劃分內(nèi)存效率要高很多。
標(biāo)記-整理算法標(biāo)記出需要回收的對(duì)象,然后讓所有存活的對(duì)象都向一段移動(dòng),將另一端的內(nèi)存區(qū)域清除掉。
分代收集算法根據(jù)新生代和老年代的不同特點(diǎn)選擇不同的算法,新生代使用復(fù)制算法,老年代使用標(biāo)記清楚或標(biāo)記整理算法,虛擬機(jī)實(shí)際使用這種算法。
內(nèi)存分配與回收策略 對(duì)象優(yōu)先在Eden上分配 GC分類Monior GC,新生代GC,指發(fā)生在新生代的垃圾收集動(dòng)作,因?yàn)镴ava對(duì)象大多都具備朝生夕滅的特點(diǎn),所以Monior GC很頻繁,速度也很快
Major GC/Full GC,老年代GC,指發(fā)生在老年代的垃圾回收動(dòng)作,一般比Monior GC慢十倍以上。
大對(duì)象直接進(jìn)入老年代大對(duì)象指需要大量連續(xù)內(nèi)存空間的Java對(duì)象,如很長(zhǎng)的字符串以及數(shù)組。直接進(jìn)入老年代避免頻繁的GC活動(dòng)。
長(zhǎng)期存活的對(duì)象將進(jìn)入老年代對(duì)象在新生代區(qū)域每熬過一次Minor GC,年齡就增加一歲(Age Count),超過15歲(默認(rèn)),就會(huì)被晉升到老年代中。
動(dòng)態(tài)年齡判定如果相同年齡的對(duì)象所占內(nèi)存大于Survivor空間的一半,年齡大于等于該年齡的對(duì)象就可以直接進(jìn)入老年代。
三.類文件結(jié)構(gòu) Class類文件的結(jié)構(gòu)一組以八位字節(jié)為基礎(chǔ)的二進(jìn)制流,各個(gè)數(shù)據(jù)項(xiàng)目嚴(yán)格按照順序緊湊地排列在Class文件之中,中間沒有任何分隔符。
儲(chǔ)存結(jié)構(gòu)無符號(hào)數(shù),用來描述數(shù)字,索引引用,數(shù)量值或UTF-8編碼的字符串
表,多個(gè)無符號(hào)數(shù)+表=表,_info結(jié)尾,Class實(shí)際上就是一張表
魔數(shù)每個(gè)Class文件的頭4個(gè)字節(jié),確定這個(gè)文件是否為一個(gè)能被虛擬機(jī)接受的Class文件。class文件的魔數(shù)是0XCAFEBABE。
Class文件的版本號(hào)緊跟魔數(shù)的四個(gè)字節(jié)確定版本號(hào):5,6字節(jié)為次版本號(hào),7,8字節(jié)為主版本號(hào)。jdk向下兼容,不向上兼容。
常量池緊隨主次版本號(hào)之后包含:
字面量文本字符串,申明為final的常量值。
符號(hào)引用
類和接口的全限定名
字段的名稱和描述符
方法的名稱和描述符
動(dòng)態(tài)連接各個(gè)字段的內(nèi)存信息,從常量池中獲得對(duì)應(yīng)的讀出引用,再在類創(chuàng)建時(shí)或運(yùn)行解析翻譯到具體的內(nèi)存地址之中。
每一項(xiàng)常量都是一個(gè)表,每個(gè)表的第一位都是一個(gè)是一個(gè)u1類型的標(biāo)志位,代表這個(gè)常量屬于哪種常量類型。
訪問標(biāo)志緊隨常量池后面,兩個(gè)字節(jié)代表訪問標(biāo)志,標(biāo)識(shí)類或接口的訪問信息。如這個(gè)Class是類還是接口,public類型等。
類索引,父類索引,接口索引集合除了接口索引是集合外,其他索引都只有一個(gè),用這三個(gè)索引確定類的繼承關(guān)系。類索引用于確定類的全限定名,父類索引用于確定父類的全限定名。
字段表集合用于描述類或接口中聲明的變量,字段包括類級(jí)變量和實(shí)例級(jí)變量,不包括方法中聲明的局部變量,描述字段的屬性如public,static,final等用一個(gè)布爾變量表示,剛好使用一個(gè)標(biāo)志位,通過引用常量池中的常量來確定。
方法表集合與字段表相似。
屬性表集合Class文件,字段表,方法表都可以攜帶自己的屬性表集合,用于描述某些場(chǎng)景專有的信息。
字節(jié)碼指令操作碼長(zhǎng)度為一個(gè)字節(jié),所以總數(shù)最多不超過256條。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/68628.html
摘要:本篇博客主要針對(duì)虛擬機(jī)的晚期編譯優(yōu)化,內(nèi)存模型與線程,線程安全與鎖優(yōu)化進(jìn)行總結(jié),其余部分總結(jié)請(qǐng)點(diǎn)擊虛擬總結(jié)上篇,虛擬機(jī)總結(jié)中篇。 本篇博客主要針對(duì)Java虛擬機(jī)的晚期編譯優(yōu)化,Java內(nèi)存模型與線程,線程安全與鎖優(yōu)化進(jìn)行總結(jié),其余部分總結(jié)請(qǐng)點(diǎn)擊Java虛擬總結(jié)上篇 ,Java虛擬機(jī)總結(jié)中篇。 一.晚期運(yùn)行期優(yōu)化 即時(shí)編譯器JIT 即時(shí)編譯器JIT的作用就是熱點(diǎn)代碼轉(zhuǎn)換為平臺(tái)相關(guān)的機(jī)器碼...
摘要:驗(yàn)證過程驗(yàn)證過程的目的是為了確保文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。二虛擬機(jī)字節(jié)碼執(zhí)行引擎虛擬機(jī)的執(zhí)行引擎自行實(shí)現(xiàn),可以自行制定指令集與執(zhí)行引擎的結(jié)構(gòu)體系。 本篇博客主要針對(duì)Java虛擬機(jī)的類加載機(jī)制,虛擬機(jī)字節(jié)碼執(zhí)行引擎,早期編譯優(yōu)化進(jìn)行總結(jié),其余部分總結(jié)請(qǐng)點(diǎn)擊Java虛擬總結(jié)上篇 。 一.虛擬機(jī)類加載機(jī)制 概述 虛擬機(jī)把描述類的數(shù)據(jù)從Clas...
摘要:與都繼承自類,在中也是使用字符數(shù)組保存字符串,,這兩種對(duì)象都是可變的。采用字節(jié)碼的好處語(yǔ)言通過字節(jié)碼的方式,在一定程度上解決了傳統(tǒng)解釋型語(yǔ)言執(zhí)行效率低的問題,同時(shí)又保留了解釋型語(yǔ)言可移植的特點(diǎn)。 String和StringBuffer、StringBuilder的區(qū)別是什么?String為什么是不可變的? String和StringBuffer、StringBuilder的區(qū)別 可變性...
摘要:最近在備戰(zhàn)面試的過程中,整理一下面試題。成員變量如果沒有被賦初值,則會(huì)自動(dòng)以類型的默認(rèn)值而賦值一種情況例外被修飾但沒有被修飾的成員變量必須顯示地賦值而局部變量則不會(huì)自動(dòng)賦值。 最近在備戰(zhàn)面試的過程中,整理一下面試題。大多數(shù)題目都是自己手敲的,網(wǎng)上也有很多這樣的總結(jié)。自己感覺總是很亂,所以花了很久把自己覺得重要的東西總結(jié)了一下。 面向?qū)ο蠛兔嫦蜻^程的區(qū)別 面向過程: 優(yōu)點(diǎn):性能比面...
摘要:編譯器只需面向,生成能理解的代碼或字節(jié)碼文件。源文件經(jīng)編譯器,編譯成字節(jié)碼程序,通過將每一條指令翻譯成不同平臺(tái)機(jī)器碼,通過特定平臺(tái)運(yùn)行。漲見識(shí),字節(jié)碼執(zhí)行過程分析。解決辦法減少默認(rèn)棧的容量來?yè)Q取更多的線程支持。 前言 JVM是java的核心和基礎(chǔ),在java編譯器和os平臺(tái)之間的虛擬處理器。它是一種基于下層的操作系統(tǒng)和硬件平臺(tái)并利用軟件方法來實(shí)現(xiàn)的抽象的計(jì)算機(jī),可以在上面執(zhí)行java的...
閱讀 1168·2023-04-26 03:02
閱讀 1198·2023-04-25 19:18
閱讀 2597·2021-11-23 09:51
閱讀 2580·2021-11-11 16:55
閱讀 2634·2021-10-21 09:39
閱讀 1713·2021-10-09 09:59
閱讀 2008·2021-09-26 09:55
閱讀 3538·2021-09-26 09:55