您好,登錄后才能下訂單哦!
JVM內(nèi)存有哪些需要注意的,針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡(jiǎn)單易行的方法。
對(duì)于C語言開發(fā)的程序員來說,在內(nèi)存管理方面,必須負(fù)責(zé)每一個(gè)對(duì)象的生命周期,從有到無。
對(duì)于Java程序員你來說,在虛擬機(jī)內(nèi)存管理的幫助下,不需要為每個(gè)new對(duì)象都匹配free操作,內(nèi)存泄露和內(nèi)存溢出等問題也不太容易出現(xiàn),不過也正是因?yàn)榘褍?nèi)存管理交給了虛擬機(jī),一旦運(yùn)行中的程序出現(xiàn)了內(nèi)存泄露問題,給排查過程造成很大困難。所以只有理解了Java虛擬機(jī)的運(yùn)行機(jī)制,才能夠運(yùn)籌帷幄于各種代碼。本文以HotSpot為例說說虛擬機(jī)的那些事。
JAVA虛擬機(jī)把管理的內(nèi)存劃分為幾個(gè)不同的數(shù)據(jù)區(qū)。
Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,主要用于存放對(duì)象實(shí)例,Java虛擬機(jī)規(guī)范中有這樣一段描述:所有的對(duì)象實(shí)例和數(shù)據(jù)都要在堆上進(jìn)行分配。為對(duì)象分配內(nèi)存就是把一塊大小確定的內(nèi)存從堆內(nèi)存中劃分出來,通常有兩種方法實(shí)現(xiàn):
1 、指針碰撞法
假設(shè)Java堆中內(nèi)存時(shí)完整的,已分配的內(nèi)存和空閑內(nèi)存分別在不同的一側(cè),通過一個(gè)指針作為分界點(diǎn),需要分配內(nèi)存時(shí),僅僅需要把指針往空閑的一端移動(dòng)與對(duì)象大小相等的距離。
2、空閑列表法
事實(shí)上,Java堆的內(nèi)存并不是完整的,已分配的內(nèi)存和空閑內(nèi)存相互交錯(cuò),JVM通過維護(hù)一個(gè)列表,記錄可用的內(nèi)存塊信息,當(dāng)分配操作發(fā)生時(shí),從列表中找到一個(gè)足夠大的內(nèi)存塊分配給對(duì)象實(shí)例,并更新列表上的記錄。
對(duì)象創(chuàng)建是一個(gè)非常頻繁的行為,進(jìn)行堆內(nèi)存分配時(shí)還需要考慮多線程并發(fā)問題,可能出現(xiàn)正在給對(duì)象A分配內(nèi)存,指針或記錄還未更新,對(duì)象B又同時(shí)分配到原來的內(nèi)存,解決這個(gè)問題有兩種方案:
1、采用CAS保證數(shù)據(jù)更新操作的原子性;
2、把內(nèi)存分配的行為按照線程進(jìn)行劃分,在不同的空間中進(jìn)行,每個(gè)線程在Java堆中預(yù)先分配一個(gè)內(nèi)存塊,稱為本地線程分配緩沖(Thread Local Allocation Buffer, TLAB);
Java棧是線程私有的,每個(gè)線程對(duì)應(yīng)一個(gè)Java棧,每個(gè)線程在執(zhí)行一個(gè)方法時(shí)會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)的棧幀(Stack Frame),棧幀負(fù)責(zé)存儲(chǔ)局部變量變量表、操作數(shù)棧、動(dòng)態(tài)鏈接和方法返回地址等信息。每個(gè)方法的調(diào)用過程,相當(dāng)于棧幀在Java棧的入棧和出棧過程。
局部變量表 用于存放方法參數(shù)和方法內(nèi)部定義的局部變量,其大小在代碼編譯期間已經(jīng)確定,在方法運(yùn)行期間不會(huì)改變。局部變量表以變量槽(Slot)為最小存儲(chǔ)單位,每個(gè)Slot能夠存放一個(gè)boolean、byte、char、shot、int、float、reference和returnAddress類型的32位數(shù)據(jù),對(duì)于64位的數(shù)據(jù)類型long和double,虛擬機(jī)會(huì)以高位對(duì)齊的方式為其分配兩個(gè)連續(xù)的Slot空間。
在方法執(zhí)行時(shí),如果是實(shí)例方法,即非static方法,局部變量表中第0位Slot默認(rèn)存放對(duì)象實(shí)例的引用,在方法中可以通過關(guān)鍵字 this 進(jìn)行訪問,方法參數(shù)按照參數(shù)列表順序,從第1位Slot開始分配,方法內(nèi)部變量則按照定義順序進(jìn)行分配其余的Slot。
class test { public int calc(int a, int b, String operation) { operation = "+"; return a + b; } public void main(String args[]) { calc(100, 200, "+"); } }
對(duì)應(yīng)的局部變量表如下:
使用 javap -c 命令查看方法calc的字節(jié)碼
其中iload_1和iload_2分別從局部變量表中的第1位和第2位中加載數(shù)據(jù)。
方法區(qū)和Java堆一樣,是所有線程共享的內(nèi)存區(qū)域,用于存放已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量和即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。
運(yùn)行時(shí)常量池是方法區(qū)的一部分,用于存放編譯期間生成的各種字面常量和符號(hào)引用。
指令計(jì)數(shù)器是線程私有的,每個(gè)線程都有獨(dú)立的指令計(jì)數(shù)器,計(jì)數(shù)器記錄著虛擬機(jī)正在執(zhí)行的字節(jié)碼指令的地址,分支、循環(huán)、跳轉(zhuǎn)、異常處理和線程恢復(fù)等操作都依賴這個(gè)計(jì)數(shù)器完成。如果線程執(zhí)行的是native方法,這個(gè)計(jì)數(shù)器則為空。
對(duì)象在內(nèi)存中布局可以分成三塊區(qū)域:對(duì)象頭、實(shí)例數(shù)據(jù)和對(duì)齊填充。
1、對(duì)象頭
對(duì)象頭包括兩部分信息:運(yùn)行時(shí)數(shù)據(jù)和類型指針,如果對(duì)象是一個(gè)數(shù)組,還需要一塊用于記錄數(shù)組長(zhǎng)度的數(shù)據(jù)。
1.1、運(yùn)行時(shí)數(shù)據(jù)包括哈希碼(HashCode)、GC分代年齡、鎖狀態(tài)標(biāo)志、線程持有的鎖、偏向鎖ID和偏向時(shí)間戳等,這部分?jǐn)?shù)據(jù)在32位和64位虛擬機(jī)中的長(zhǎng)度分別為32bit和64bit,官方稱為"Mark Word"。Mark Word被設(shè)計(jì)成非固定的數(shù)據(jù)結(jié)構(gòu),以實(shí)現(xiàn)在有限空間內(nèi)保存盡可能多的數(shù)據(jù)。
32位的虛擬機(jī)中,對(duì)象未被鎖定的狀態(tài)下,Mark Word的32bit中25bit存儲(chǔ)對(duì)象的HashCode、4bit存儲(chǔ)對(duì)象分代年齡、2bit存儲(chǔ)鎖標(biāo)志位、1bit固定為0,具體如下:
其它狀態(tài)(輕量級(jí)鎖定、重量級(jí)鎖定、GC鎖定、可偏向鎖)下Mark Word的存儲(chǔ)內(nèi)容如下:
1.2、對(duì)象頭的類型指針指向該對(duì)象的類元數(shù)據(jù),虛擬機(jī)通過這個(gè)指針可以確定該對(duì)象是哪個(gè)類的實(shí)例。
2、實(shí)例數(shù)據(jù)
實(shí)例數(shù)據(jù)就是在程序代碼中所定義的各種類型的字段,包括從父類繼承的,這部分的存儲(chǔ)順序會(huì)受到虛擬機(jī)分配策略和字段在源碼中定義順序的影響。
3、對(duì)齊填充
由于HotSpot的自動(dòng)內(nèi)存管理要求對(duì)象的起始地址必須是8字節(jié)的整數(shù)倍,即對(duì)象的大小必須是8字節(jié)的整數(shù)倍,對(duì)象頭的數(shù)據(jù)正好是8的整數(shù)倍,所以當(dāng)實(shí)例數(shù)據(jù)不夠8字節(jié)整數(shù)倍時(shí),需要通過對(duì)齊填充進(jìn)行補(bǔ)全。
關(guān)于JVM內(nèi)存有哪些需要注意的問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。