JVM 運(yùn)行時(shí)數(shù)據(jù)區(qū)詳解,寫(xiě)得非常好!

Java技術(shù)棧
www.javastack.cn
關(guān)注閱讀更多優(yōu)質(zhì)文章
在接下來(lái)的幾天想總結(jié)下,JVM相關(guān)的一些內(nèi)容,比如下面的這三個(gè)內(nèi)容算是比較核心知識(shí)點(diǎn)了
3.類加載機(jī)制: 虛擬機(jī)首先需要把編譯完成的字節(jié)碼文件通過(guò)類加載器來(lái)加載到運(yùn)行時(shí)數(shù)據(jù)區(qū)域

一個(gè)段Java代碼的生命周期都會(huì)少不了上圖這幾個(gè)步驟,也就是Java代碼首先會(huì)被編譯成字節(jié)碼文件,之后被類加載器加載到運(yùn)行時(shí)數(shù)據(jù)區(qū)域,以及運(yùn)行,垃圾收集器回收對(duì)象等等。
但今天我想介紹第一個(gè)知識(shí)點(diǎn)《運(yùn)行時(shí)數(shù)據(jù)區(qū)域》
1 運(yùn)行時(shí)數(shù)據(jù)區(qū)
1.1 PC寄存器
全名叫做 Program Counter Register 既然是叫做寄存器了那么肯定是需要存東西,那到底存的是什呢?
由于JVM同時(shí)可以處理多個(gè)線程所以就涉及到一些線程調(diào)度,當(dāng)cpu暫停運(yùn)行線程A把時(shí)間片讓給線程B的時(shí)候我們需要保存線程A被暫停執(zhí)行前的一些現(xiàn)場(chǎng)狀態(tài),需要記錄當(dāng)前執(zhí)行到那一行字節(jié)碼了,所以具備保存現(xiàn)場(chǎng)的功能。
每條線程都有自己的pc寄存器,在任意時(shí)刻虛擬機(jī)只會(huì)執(zhí)行一個(gè)方法,如果執(zhí)行的是方法不是native方法 pc寄存器則保存指向當(dāng)前執(zhí)行字節(jié)碼的指令地址,如果執(zhí)行的是native方法 pc寄存器會(huì)保存undefined。
1.2 java虛擬機(jī)棧
虛擬機(jī)棧也是每條線程私有的區(qū)域,里頭存儲(chǔ)棧幀(Frame),后面會(huì)重點(diǎn)介紹棧幀算是重點(diǎn)內(nèi)容。方法的調(diào)用與返回基于棧幀來(lái)實(shí)現(xiàn)的。
1.3 虛擬機(jī)堆
堆內(nèi)存里的對(duì)象不會(huì)被顯式的回收,而是由垃圾回收器回收,為了配合垃圾收集器的特性我們可以把堆分為年輕代和老年代。

年輕代又分了Eden和survivor區(qū),主要是為了配合垃圾回收算法而這么搞得。
1.4 方法區(qū)和運(yùn)行時(shí)常量池
在Java虛擬機(jī)中 方法區(qū)是可提供各個(gè)線程共享的運(yùn)行時(shí)內(nèi)存區(qū)域,它存儲(chǔ)了每一個(gè)類的結(jié)構(gòu)信息,例如運(yùn)行時(shí)常量池,字段和方法數(shù)據(jù),構(gòu)造函數(shù)和普通函數(shù)的字節(jié)碼內(nèi)容,一句話總結(jié)就是存儲(chǔ)元數(shù)據(jù)地方
運(yùn)行時(shí)常量池是class文件中每個(gè)類或接口常量池表的表示形式。它包括了若干不同的常量,比如 從編譯期可知的數(shù)值字面量到運(yùn)行時(shí)才能解析獲得的方法或字段引用等等。
創(chuàng)建時(shí)機(jī)
每個(gè)運(yùn)行時(shí)常量池都在Java虛擬機(jī)的方法區(qū)中分配,在加載類和接口到虛擬機(jī)之后創(chuàng)建對(duì)應(yīng)的運(yùn)行時(shí)常量池

1.5 本地方法棧
如果我們想再Java底層里調(diào)用別的語(yǔ)言代碼的話就需要用到別的方法棧了,比如Java虛擬機(jī)的實(shí)現(xiàn)會(huì)用到傳統(tǒng)的棧(C stack)來(lái)調(diào)用native方法,這個(gè)就是本地方法棧的應(yīng)用,當(dāng)然這個(gè)不是必須實(shí)現(xiàn)的,完全取決于虛擬機(jī)的實(shí)現(xiàn)。
2 棧幀:
首先看下棧幀在虛擬機(jī)內(nèi)存中在什么位置,

2.1 局部變量表 (Local variable)
每個(gè)棧幀內(nèi)部都包含一組稱為局部變量表的列表,變量表的長(zhǎng)度在編譯期決定。另外關(guān)注公眾號(hào)Java技術(shù)棧在后臺(tái)回復(fù)JVM獲取一份46頁(yè)的JVM調(diào)優(yōu)教程。
一個(gè)局部變量可以存儲(chǔ)一個(gè)基本數(shù)據(jù)類型或一個(gè)對(duì)象引用(referance),returnAddress的數(shù)據(jù)。存儲(chǔ)long或double需要兩個(gè)局部變量才能存儲(chǔ)。
當(dāng)虛擬機(jī)要使用局部變量表里的數(shù)據(jù)時(shí)通過(guò)索引來(lái)定位,默認(rèn)從0開(kāi)始,由于long和double占用兩個(gè)局部變量所以它的索引較特殊,取決于最小的那個(gè)值,比如某個(gè)long類型數(shù)據(jù)在索引n和n+1里存儲(chǔ)了,那么它對(duì)應(yīng)的索引值就是n.
虛擬機(jī)通過(guò)局部變量表來(lái)完成方法調(diào)用時(shí)的參數(shù)傳遞。如果是類方法,它的參數(shù)依次從0開(kāi)始的位置傳遞到局部變量表,如果是實(shí)例方法則第0位置存儲(chǔ)所在對(duì)象的引用(this),從1開(kāi)始傳遞參數(shù)。
2.2 操作數(shù)棧 (Operating Stack)
操作數(shù)棧是屬于棧幀中的棧,其實(shí)它的全名叫做當(dāng)前棧幀的初操作數(shù)棧。棧,棧幀,操作數(shù)棧的關(guān)系需要梳理清楚:
棧:是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一個(gè)邏輯區(qū)域,里面存儲(chǔ)了一個(gè)個(gè)棧幀。
棧幀:棧幀代表一個(gè)方法的整個(gè)生命周期,里頭存儲(chǔ)了局部變量表,操作數(shù)棧,動(dòng)態(tài)鏈接
操作數(shù)棧: 剛剛創(chuàng)建時(shí)操作數(shù)棧是空的。虛擬機(jī)提供一些指令從局部變量表把一些常量或者變量值加載到操作數(shù)棧,也提供了從操作數(shù)棧取走數(shù)據(jù)的指令。
調(diào)用方法時(shí)操作數(shù)棧用來(lái)準(zhǔn)備調(diào)用方法參數(shù)以及接受方法的返回結(jié)果。
2.3 動(dòng)態(tài)鏈接 (Dynamic Linking)。
動(dòng)態(tài)鏈接是用來(lái)完成運(yùn)行時(shí)綁定操作的。在棧幀中有一個(gè)指向常量池的當(dāng)前類的一個(gè)引用。在class文件里一個(gè)方法要是調(diào)用其他方法或者方法其他成員變量,則需要通過(guò)符號(hào)引用來(lái)表示。
動(dòng)態(tài)鏈接的作用就是將符號(hào)引用轉(zhuǎn)換為直接引用。
類加載的過(guò)程中將要解析尚未被解析的符號(hào)引用,并且把對(duì)變量的訪問(wèn)轉(zhuǎn)換為正確的偏移量。
點(diǎn)擊「閱讀原文」獲取面試題大全~

