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

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

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

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

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

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

