程式計數器是jvm執行程式的管線,是用來存放一些指令的,本地方法堆疊是jvm作業系統方法所使用的棧,而虛擬機器棧是用來執行程式碼的棧,在方法區中有類變量,類信息,方法信息,常量池(符號的引用,以表的形式存在的),堆是虛擬機執行程序代碼的所用的堆。
儲存的是編譯產生的各種字面量和符號參考。在計算機科學中,字面量是用來表達原始碼中固定值的表示法;而符號引用是一組符號用來描述所引用的目標,可以是任何形式的字面量,只要使用時能夠無歧義的定位到目標就行。
常數池是以表的形式存在(表是用來儲存字串值的,不儲存符號參考),實際上可以分兩種,一種為靜態常數池,另一種為運行時常量池,共有11中常量表,常量池的每一個常數代表一張表。
CONSTANT_Utf8 |
1 |
UTF-8編碼的Unicode字串 |
CONSTANT_Integer |
3 |
int類型的字面值 |
CONSTANT_Float |
4 |
float類型的字面值 |
#CONSTANT_Long |
5 |
long類型的字面值 |
CONSTANT_Double |
6 |
# double類型的字面值 |
CONSTANT_Class |
7 |
對一個類別或是介面的符號引用 |
CONSTANT_String |
8 |
String類型的字面值的參考 |
CONSTANT_Fieldref |
9 |
#對一個欄位的符號 |
CONSTANT_Methodref |
10 |
對一個類別中方法的符號套用 |
# CONSTANT_InterfaceMethodref |
11 |
對一個介面中方法的符號引用 |
##CONSTANT_NameAndType | 12 | 對一個欄位或方法的部分符號引用 |
常數池
Integer integer1 = 127;
Integer integer2 = 127;
System.out.println(integer1 == integer2);
// true
Integer integer1 = 128;
Integer integer2 = 128;
System.out.println(integer1 == integer2);
// false
在Java中符號「==」是用來比較位址,符號「equals」預設是與符號「==」一樣,都是用來比較位址的。
String string1 = "dashu";
String string2 = "dashu";
System.out.println(string1==string2);
// true
String string1 = "dashu";
String string3 = new String("dashu");
System.out.println(string1 == string3);
// false
String str = new String("dashu"); 建立了幾個物件呢?
答案是:2個或1個。
在new String("dashu");,如果這個「dashu」字面值已經出現在常數池中,那麼就只出創建一個對象,如果沒有就創建兩個對象。
原理: 出現了字面量“dashu”,系統會到字串常數池中查找是否有相同的字串存在,如果有,就不會創建新的物件了,否則就會用字面量值“dashu”,建立一個String物件。而new String("dashu"),有關鍵字new的存在,就表示它一定會建立一個新的對象,然後呼叫接收String參數的建構器來初始化。
如果改為string1 == string3.intern()結果為true,因為傳回的是常數池裡面字面值的位址。
堆疊:執行緒堆疊與本機方法堆疊
// 源码
public class Object{
private static native void registerNatives();
static{
registerNatives();
}
}
// 源码
public boolean equals(Object obj){
return (this == obj);
}
// 源码
public String toString(){
return getClass().getName() + "@" + Integer.toHexString(hasCode());
}
// 源码
protected native Object clone() throws CloneNotSupportedException;
有native
修飾符修飾的是透過JNI
來呼叫c
語言或是c
執行的。
所有的類別都是Object
的子類別。
万物皆对象
// 源码注解
Class {@code Object} is the root of the class hierarchy.
Every class has {@code Object} as a superclass.
All objects, including arrays, implements the methods of this class.
@ see java.lang Class
@ since JDK1.0
常數池:
Class檔案中儲存所有常數
在Java中說過常數池可以分成兩種形態,靜態常數池和運行時常數池。
靜態常數池就是class檔案中的常數池有字串字面量,類別訊息,方法的資訊等,佔用了class檔案較大部分的空間,在常數池中主要存放的是字面量和符號引用量。
運行時常數池是java虛擬機器在完成類別載入後的操作,將class檔案中的常數池載入到記憶體中,並保證在方法區,我們口中的常數池是在方法區中運作的常數池,運行時常數池具有動態性,在運作期間也能產生新的常數放入池中,就是上方寫過的程式碼。常量不一定要在編譯期間產生,也可以在運作期間產生新的產量放入池中。
如下解析:
Java虛擬機器jvm在執行某個類別的時候,要經過類別從載入到記憶體中,到卸載為止。
整個過程為 加載,驗證,準備,解析,初始化,使用,卸載。
加載,
驗證,class檔案的版本是否能相容於目前的Java虛擬機器版本,然後class檔案要滿足虛擬機器的規格。
準備,需要準備什麼呢?
就是要進行類別成員的初始化為初始值,其中為final修飾的類別變數除外,final變數就直接初始化為變數值,而類別成員不一樣。
解析,什麼是解析呢?
就是把符號引用解析為直接引用,就是我們變數xxx,這種代表變成直接引用,什麼是直接引用呢?就是記憶體位址,如我們常見的xxx0203r0e,這種。
初始化,把關於static修飾的變數或是static靜態程式碼區塊依照順序組成建構器來初始化變數。
使用,
卸載
當類別載入到記憶體後,jvm會將class常數池中的內容存放到運行時常數池中,所以執行時間常數池每個類別都有一個的。
class常數池是存放字面量和符號的引用,是物件的符號引用值,經過解析就是把符號引用解析為直接引用,在編譯階段存放的是常數的符號引用,進行解析後就是直接引用了。然後在全域常數池中保證每個jvm只有一份,存放的是字串常數的直接引用值。
如果改為`string1 == string3.intern()`結果為true,因為傳回的是常數池裡面字面值的位址。
String類別的intern()方法,會在常數池中尋找是否有一份equal()相等的字串。
String string1 = "dashu";
String string3 = new String("dashu");
System.out.println(string1==string3.intern());
如果常數池中沒有這個“dashu”
字面量,那麼就先把這個字面量“dashu”
值,先放入到常數之後,再返回常量表的地址。
常數池優點
常數池可以避免因頻繁的創建和銷毀對象,從而導致系統性能的降低,也實現了對象的共享,即可以節省內存空間,也可以節省運轉的時間。