對象的創建
- Object object = new Object();
- 虛擬機接收new指令
- 虛擬機根據new的參數在常量池中定位一個類的符號引用
- 常量池:用於存儲編譯器所生產字面量和符號引用量(圖片來自於wangbiao007博主的博客方法區和常量池)
- 如果沒有找到這個符號引用,說明類還沒有被加載,則進行類的加載,解析和初始化。
- 類加載檢查後,虛擬機將爲對象分配內存(位於堆中)。分配內存有下面兩種方式
- 指針碰撞:堆內存中的空閒空間如果十分的規整,使用與未使用的空間全部爲連續,只需移動一下指針就可以了
- 空閒列表:針對堆內存中的空間零散的存在,虛擬機維護着一個列表,記錄着哪裏被分配了,哪裏還空閒。,分配內存的時候,只需要從表中查找。
- 內存分配完後,虛擬機將分配的內存初始化爲零值(不包括對象頭),初始化零值是保證了對象的實例字段在Java代碼中不賦初始值就可以直接使用,程序能訪問到這些字段的數據類型所對應的零值。這裏順便說下Java對象的結構。
對象由對象頭(Header)、實例數據(InstanceData)和對齊填充(Padding),3部分組成。 - 最後調用對象的<init>方法
對象的結構(對象的內存模型)
- 對象頭
- 標誌信息:用來存放對象一些固有屬性的狀態,這些屬性從對象創建就有,而不是 Java 的使用者定義的:哈希碼(對象的唯一標識)、對象的分代年齡(雨垃圾回收有關)、線程持有的鎖、鎖的狀態、偏向線程 ID、偏向時間戳、數組長度(如果該對象是數組,會有數組長度信息)。
- 元信息指針:指向方法區中類元信息的指針。
- 實例數據
- 實例的數據信息存放的是一些對 Java 使用者真正有效的信息,也就是類中定義的各個字段,其中還包括從父類繼承的字段。
- 對齊填充
- 對其填充這段內存段存在與否取決於前面兩部分的長度,爲了保證對象內存模型的長度爲 8 字節的整數倍,這也是虛擬機自動內存管理的要求。
對象的訪問定位
虛擬機棧是Java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀(Stack Frame)用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。
局部變量表存放了編譯期可知的各種基本數據類型(long,boolean,int,byte,short,long,double,float)、returnAddress類型(指向了一條字節碼指令的地址)和對象引用(reference類型,它不代表對象本身,可能是一個指向對象起始地址的引用指針,也可能指向一個對象代表的句柄或其他與此對象相關的位置)
簡單來說就是通過棧幀中局部變量表所存儲的對象引用來對堆內存中的對象實例進行訪問或操作的。棧幀中有個對象引用的指針, 通過各種方法指向了堆內存中的對象實例。
而這各種方法中,主要有兩種
- 通過句柄方式訪問
Java堆中劃分出一塊內存作爲句柄池,reference中存儲的是對象的句柄地址,而句柄中包含了對象實例數據與類型數據各自的具體地址信息。
優點:
當對象移動的時候(垃圾回收的時候移動很普遍),這樣值需要改變句柄中的指針,但是棧中的指針不需要變化,因爲棧中存儲的是句柄的地址
缺點:
需要進行二次定位,尋找兩次指針,開銷相對於更大一些 - 直接指針訪問方式
reference直接指向了對象實例數據,那麼java堆對象分佈中就必須考慮如何放置訪問類型數據的相關信息,reference存儲的直接就是對象地址。
優點:速度快,不需要和句柄一樣指針定位的開銷
參考博客
https://www.cnblogs.com/YYfish/p/6722258.html
https://blog.csdn.net/sinat_34657451/article/details/52038669