《深入理解java虛擬機器》第二章筆記
阿新 • • 發佈:2019-01-29
1. 執行時資料區域
名稱 | 是否共享 | 作用 | 存在的異常 |
---|---|---|---|
程式計數器 | 執行緒私有 | 如果執行的是java方法,這個計數器記錄的是正在執行的虛擬機器位元組碼指令的地址 | |
java虛擬機器棧 | 執行緒私有 | 每個方法在執行的同時會建立一個棧幀(區域性變量表,運算元棧等),方法的呼叫過程,對應一個棧幀在虛擬機器棧中的出入棧 | StackOverflowError,OutofMemoryError |
本地方法棧 | 執行緒私有 | 作用與虛擬機器棧類似,但是它是為Native方法服務的 | StackOverflowError,OutofMemoryError |
java堆 | 執行緒共享 | 所有的物件例項以及陣列都要在堆上分配,隨著技術的發展,現在變得不是那麼絕對了,垃圾回收比較多 | OutofMemoryError |
方法區 | 執行緒共享 | 用於儲存已被虛擬機器載入的類資訊,常量,靜態變數等。垃圾回收在此比較少 | OutofMemoryError |
執行時常量池 | 執行緒共享 | 屬於方法區,用於存放編譯期生成的 各種字面量和符號引用 | OutofMemoryError |
2. 物件探究
2.1 物件的建立
類載入檢查:
- 遇到new指令時,檢查這個指令的引數是否能在常量池中定位到一個類的符號引用。
檢查這個符號引用代表的類是否已被載入,解析和初始化過.
- 分配記憶體:等同於把一塊確定大小的記憶體從java堆中劃分出來
- 指標碰撞:假設java堆中的記憶體是絕對規整的,所有用過的記憶體放在一邊,空閒的記憶體放在另一邊,那麼就可以用一個指標向空閒空間挪動一段與物件大小相等的距離。
- 空閒列表:假設記憶體是不規整的,那虛擬機器就會維護一個列表,記錄哪些記憶體塊是可用的,然後去找一塊足夠大的劃分給物件例項,並更新列表記錄。
ps:還要考慮併發問題,有兩種解決方案
- CAS來保證更新操作原子性
把記憶體分配按照執行緒劃分到不同的空間之中,也就是給每一個執行緒預先分配TLAB(本地執行緒分配快取),只有TLAB用完,才會同步鎖定
初始化為零值
進行必要設定
比如這個物件是哪個類的例項,物件的雜湊碼等,還有是否啟用偏向鎖等,主要是對物件頭進行設定
2.2物件的記憶體佈局
2.2.1 物件頭
第一部分用於儲存物件自身的執行時資料
如雜湊碼,GC分代年齡,鎖狀態標識等
第二部分是型別指標
用來確定這個物件是哪個類的例項,如果物件是陣列的話,還得有一塊用於記錄陣列長度的資料。
2.2.2 例項資料
- 儲存的是有效資訊,也就是所定義的各種型別的欄位內容。
- 這部分的儲存順序會受 虛擬機器分配策略引數,欄位在Java原始碼中的定義順序的影響
2.2.3 對齊填充
這部分沒什麼含義的,主要起著佔位符的作用,比如物件的大小必須是8位元組的整數倍,當物件例項資料部分沒有對齊時,就要通過它來填充
2.3物件的訪問定位
主流的訪問方式有 使用控制代碼 和直接指標 這兩種
1. 使用控制代碼
- java 堆劃分出一塊記憶體空間來作為控制代碼池,reference中存的就是物件的 控制代碼地址
最大的好處是reference中儲存的是穩定的控制代碼地址,在物件被移動時只會改控制代碼中的例項資料指標,而reference本身不需要修改
2. 直接指標
reference中存的就是物件地址
最大的好處是速度更快,節省了一次指標定位的時間開銷。
本書討論的虛擬機器是sun hotspot,它用的是直接指標方式