JVM內(nèi)存區(qū)域
jvm 按照線程安全進(jìn)行分類(lèi):
- 線程私有: 程序計(jì)數(shù)器,Java虛擬機(jī)棧,本地方法棧
- 線程共享: Java堆,方法區(qū),運(yùn)行時(shí)常量池,直接內(nèi)存(嚴(yán)格來(lái)說(shuō)不是屬于JVM的一部分)
程序計(jì)數(shù)器
字節(jié)碼解釋器通過(guò)這個(gè)計(jì)數(shù)器來(lái)選擇下一條要執(zhí)行字節(jié)碼指令。程序計(jì)數(shù)器是唯一一個(gè)不會(huì)出現(xiàn)OOM的內(nèi)存區(qū)域
Java虛擬機(jī)棧
虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存區(qū)域,每個(gè)方法執(zhí)行的同時(shí)會(huì)創(chuàng)建一個(gè)棧幀,棧幀用于存儲(chǔ)局部變量表,操作數(shù)棧,動(dòng)態(tài) 鏈表,方法出口等
局部變量表用于存儲(chǔ)編譯期的各種基本數(shù)據(jù)類(lèi)型,對(duì)象引用,這里常常跑出的異常是StackOverFlowError
Java堆
存放對(duì)象,現(xiàn)代的Jvm實(shí)現(xiàn)一般都會(huì)在JAVA堆上進(jìn)行分代存儲(chǔ),這樣能更加優(yōu)于GC時(shí)的效率 Eden(新生代)
方法區(qū)
用于存儲(chǔ)已加載過(guò)的類(lèi)的信息,常量,靜態(tài)變量,即時(shí)編譯器編譯后的代碼等數(shù)據(jù)
運(yùn)行時(shí)常量池
運(yùn)行時(shí)常量池是方法區(qū)的一部分
虛擬機(jī)中的對(duì)象
對(duì)象的創(chuàng)建
- 加載對(duì)應(yīng)的類(lèi): 碰到
new關(guān)鍵字,確定對(duì)應(yīng)的 類(lèi)是否加載過(guò),如果沒(méi)有加載,加載對(duì)應(yīng)的類(lèi) - 分配空間: 類(lèi)加載過(guò)程中,會(huì)確定,類(lèi)對(duì)應(yīng)的對(duì)象的存儲(chǔ)信息等
內(nèi)存分配的動(dòng)作按照 線程劃分在每個(gè)線程的 Thread local allocation buffer 中,用于頻繁創(chuàng)建對(duì)象的緩存 - 設(shè)置對(duì)象的必要信息: HashCode,鎖信息,GC分代信息等
對(duì)象的內(nèi)存布局
對(duì)象在內(nèi)存中分為三塊區(qū)域: 對(duì)象頭,實(shí)例數(shù)據(jù),對(duì)齊填充
- 對(duì)象頭: 存儲(chǔ)HashCode,GC分代年齡,鎖狀態(tài)標(biāo)志,線程持有的鎖,偏向線程ID,偏向時(shí)間戳,類(lèi)型指針,如果是數(shù)組,還有一塊存儲(chǔ)數(shù)組長(zhǎng)度的信息
對(duì)象的訪問(wèn)定位
Java程序需要通過(guò)棧上的reference數(shù)據(jù)來(lái)操作堆上的具體對(duì)象
- 句柄訪問(wèn): java堆上開(kāi)辟一塊存儲(chǔ)區(qū)域?qū)iT(mén)作為句柄池,句柄中包含了 實(shí)例數(shù)據(jù)與類(lèi)型數(shù)據(jù)各自的具體地址信息
- 直接地址訪問(wèn)
區(qū)別: 使用句柄訪問(wèn)的好處是reference中存儲(chǔ)的穩(wěn)定的句柄地址,在對(duì)象被移動(dòng)(GC 過(guò)程),只會(huì)改變實(shí)例數(shù)據(jù)指針,而reference本身不會(huì)改變。 但是相對(duì)于直接指針訪問(wèn)會(huì)比較慢2