每日一題,進步一點。
上題,搞起
關於堆和棧,下列說法錯誤的是
A 每個被調用的方法都會在虛擬機棧創建一個棧幀
B 每個棧幀中包括局部變量表、操作數棧、動態連結、方法返回地址
C 方法區可以指向堆區,堆區不可以指向方法區
D 堆區的對象裡面僅僅存放了自身的實例數據
答案:CD
解析
首先,我們了解一下虛擬機棧,顧名思義,它是一個棧的數據結構,裡面存放的是一個個棧幀,什麼叫棧幀?
每個棧幀對應一個被調用的方法,可以理解為一個方法的運行空間,看一下棧幀的內部結構,結合以下代碼
public static void main(String[] args) {
calc(10,3);
}
public static int calc(int op1,int op2){
int result=op1+op2;
return result;}
首先來看每個區域的定義:
局部變量表(Local Variables):方法中定義的局部變量以及方法的參數存放在這張表中 局部變量表中的變量不可直接使用,如需要使用的話,必須通過相關指令將其加載至操作數棧中作為操作數使用
操作數棧(Operand Stack):以壓棧和出棧的方式存儲操作數的
動態連結(Dynamic Linking):每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用,持有這個引用是為了支持方法調用過程中的動態連結。
方法返回地址(Return Address):當一個方法開始執行後,只有兩種方式可以退出,一種是遇到方法返回的字節碼指令;一種是遇見異常,並且 這個異常沒有在方法體內得到處理
結合上面的程序,作為局部變量的result,op1,op2,是放在局部變量表中的,執行運算時,會把常量池中的10和3拿過來放到操作數棧中進行相加,然後再賦值給result,最終把result返回出去,或者發生異常,跳出這個方法。
堆、棧、方法區的相互引用
棧指向堆如果在棧幀中有一個變量,類型為引用類型,比如Object obj=new Object(),這時候就是典型的棧中元素指向堆中的 對象。
2. 方法區指向堆
方法區中會存放靜態變量,常量等數據。如果是下面這種情況,就是典型的方法區中元素指向堆中的對象
private static Object obj=new Object();
3.堆指向方法區
堆指向方法區,是不是很難理解,我們想一想下面這個問題
方法區中會包含類的信息,堆中會有對象,那怎麼知道對象是哪個類創建的呢?
我們堆中的對象,不僅僅記錄對象本身的屬性信息,還有其他我們看不見的東西,一張圖說明一切
一個Java對象在內存中包括3個部分:對象頭、實例數據和對齊填充
其中對象頭中間的Class Pointer就是指向了方法區的元數據地址。
對象頭中還有一個重要的區域,Mark Word,對象鎖的狀態,分代年齡,都在這兒。
實例數據,就不用說了,對象的成員變量的值都在這兒,我們最為熟知的部分。
對齊填充,做個了解,對齊填充並不是必然存在的,也沒有特別的含義,它僅僅起著佔位符的作用。由於HotSpot VM的自動內存管理系統要求對象起始地址必須是8位元組的整數倍,換句話說就是對象的大小必須是8位元組的整數倍。對象頭正好是8位元組的倍數(1倍或者2倍),因此當對象實例數據部分沒有對齊的話,就需要通過對齊填充來補全
拓展
堆可以指向棧嗎?