方法區
所有線程共享方法區,因此設計方法區爲線程安全的.
方法區包含:
1. 被裝載類型的信息
a) 類還是接口
b) 類名全稱,父類全稱,所有接口全稱列表
c) 訪問修飾符
2. 靜態變量
3. 還有以下信息
a) 該類型的常量池
b) 字段信息
字段名,字段的類型,字段的修飾符(字段在類或者接口中的聲明順序也保存)
c) 方法信息
方法名,方法的返回類型,方法參數的數量和類型(按聲明順序),方法的修飾符
d) 除了常量以外的所有類(靜態)變量
編譯時常量(final聲明以及用編譯時已知的值初始化的類變量)會複製它的所有常量到自己的常量池中或嵌入到它的字節碼中
e) 一個到類ClassLoader的引用
當某個類型引用另一個類型的時候,虛擬機會請求裝載發起引用類型的類裝載器來裝載被引用的類型.
f) 一個到Class類的引用
g) 方法表
實例:
class Lava{
privateint speed = 5;
voidflow(){
}
}
class Volcano{
publicstatic void main(String[] args){
Lavalava = new Lava();
lava.flow();
}
}
流程:
找到並讀入相應的class文件”Volcano.class”
從導入的class文件裏的二進制數據中提取類型信息至方法區中
執行方法區字節碼,執行mian(),此時不會加載引用類(延遲加載)
會一直持有指向當前類的常量池(方法區中的一個數據機構)的指針
爲列在常量池第一項的類分配足夠的內存
檢查Lava類是否被裝載,查找並裝載,讀取二進制數據的類型信息至方法區中
使用直接引用替換符號引用
使用直接引用指針訪問Lava類型信息(此前放置在方法區中),分配堆空間
初始化實例的變量speed爲默認值0,如果超類Object也有實例變量,同樣初始化
把新生成的Lava對象的引用壓到棧中(Java棧是由許多棧幀組成,一個棧幀包含一個Java方法調用的狀態)
初始化speed爲5
調用Lava對象引用的flow()方法
堆
堆包含:
Java程序在運行時創建的所有類實例或數組都放在同一個堆中.
堆上的對象數據還有一個邏輯部分,對象鎖.(解決同步問題)
每個java對象邏輯上還與實現等待集合的數據相關聯.
關於垃圾收集器的數據
堆設計方法
1. 把堆分爲兩個部分:一個句柄池,一個對象池
好處:有利於堆碎片的整理
壞處:每次訪問對象的實例變量都要經過兩次指針傳遞
2. 對象指針直接指向一組數據
棧
局部變量區
class Example3a{
public static int runClassMethod(int I,long l,float f,double d,object o,byte b){
return 0;
}
public int runInstanceMethod(char c,double d,short s,boolean b){
return 0;
}
}
在java中,所有的對象都按引用傳遞,並且都存儲在堆中,永遠都不會在局部變量區或操作數棧中發現對象的拷貝,只會有對象引用.
byte,short,char和boolean在局部變量區都被轉換成int,他們在棧幀中的時候都是當作int來進行處理的,只有當它被存回堆或方法區時,纔會轉換會原來的類型.
靜態方法雖然是類共享的,但是和普通方法一樣也是在棧中執行.操作數棧
壓棧和出棧
要使用保存在局部變量中的值之前,必須先將它從壓入操作數棧.
舉例來說,用一個局部變量除另外一個,虛擬機必須把它們都壓入棧,執行除法,然後把結果重新保存到局部變量.要把數組元素或對象的字段保存到局部變量中,虛擬機必須先把值壓入棧,然後保存到局部變量中.要把保存在局部變量中的值賦予數組元素或者對象字段,虛擬機必須按照相反的步驟操作.它首先必須把局部變量的值壓入棧,然後從棧中彈出,在放入位於堆上的數組元素或對象字段中.
幀數據區
負責常量池解析,正常方法返回以及異常派發機制.
每當虛擬機要執行某個需要用到常量池數據的指令時,它都會通過幀數據區中指向常量池的指針來訪問它.常量池中對類型,字段和方法的引用在開始時都是符號.當虛擬機在常量池中搜索的時候,如果遇到指向類,接口,字段或者方法的入口,假若它們仍然是符號,虛擬機那時候纔會(也必須進行解析).