【JVM】創建對象時虛擬機做了什么?


我們都知道,在語言層面上,創建對象通常僅僅是一個new關鍵字而已。但是在虛擬機中,對象的創建是怎樣的一個過程呢?

虛擬機在遇到一條new指令時,首先將去檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,並且檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果沒有,那必須先執行相應的類加載過程(具體過程這里不做詳解)。在類加載檢查通過后,接下來虛擬機將為新生對象分配內存。對象所需內存的大小在類加載完成后便可完全確定。,為對象分配空間的任務等同於把一塊確定大小的的內存從java堆里划分出來。假設java堆里內存是絕對規整的,所有用過的內存都放在一邊,空閑的內存放在另一邊,中間放着一個指針作為分界點的指示器,那所分配內存就僅僅是把那個指針向空閑空間那邊挪動一段與對象大小相等的距離,這種分配方式稱為“指針碰撞”(Bump the Pointer)。如果Java堆中的內存並不是規整的,已使用的內存和空閑的內存相互交錯,那就沒有辦法簡單地進行指針碰撞了,虛擬機就必須維護一個列表,記錄上哪些內存塊是可用的,在分配的時候從列表中找到一塊足夠大的空間划分給對象實例,並更新列表上的記錄,這種分配方式稱為“空閑列表”。選擇哪種分配方式由Java堆是否規整決定,而java堆是否規整又由所采用的垃圾收集器是否帶有壓縮整理功能決定。

除如何划分可用空間之外,還有另外一個需要考慮的問題是對象創建在虛擬機中是非常頻繁的行為,即使是僅僅修改一個指針所指向的位置,在並發情況下也並不是線程安全的,可能出現正在給對象A分配內存,指針還沒來得及修改,對象B又同時使用了原來的指針來分配內存的情況。解決這個問題有兩種方案,一種是對分配內存空間的動作進行同步處理——實際上虛擬機采用CAS配上失敗重試的方式保證更新操作的原子性;另一種是把內存分配的動作按照線程划分在不同的空間之中進行,即每個線程在java堆中預先分配一小塊內存,稱為本地縣城分配緩沖(Thread Local Allocation Buffer,TLAB)。哪個線程要分配內存,就在哪個線程的TLAB上分配,只有TLAB用完並分配新的TLAB時,才需要同步鎖定。

內存分配完成后,虛擬機需要將分配到的內存空間都初始化為零值,如果使用TLAB,這一工作過程就可以提前至TLAB分配時進行。這一步操作保證了對象的實例字段在Java代碼中可以不賦初始值就直接使用,程序能訪問到這些字段的數據類型所對應的零值。(所以在實際編碼時,用到的變量需要聲明但是不一定要賦值)


以上內存摘自《深入理解Java虛擬器》這本書的部分內容。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com