Hotspot虚拟机的对象
?
創(chuàng)建
Step1:類加載檢查
虛擬機遇到一條 new 指令時,首先將去檢查這個指令的參數(shù)是否能在常量池中定位到這個類的符號引用,并且檢查這個符號引用代表的類是否已被加載過、解析和初始化過。如果沒有,那必須先執(zhí)行相應的類加載過程。
Step2:分配內(nèi)存
在類加載檢查通過后,接下來虛擬機將為新生對象分配內(nèi)存。對象所需的內(nèi)存大小在類加載完成后便可確定,為對象分配空間的任務等同于把一塊確定大小的內(nèi)存從 Java 堆中劃分出來。分配方式有 “指針碰撞” 和 “空閑列表” 兩種,選擇那種分配方式由 Java 堆是否規(guī)整決定,而 Java 堆是否規(guī)整又由所采用的垃圾收集器是否帶有壓縮整理功能決定。
內(nèi)存分配的兩種方式:
選擇以上兩種方式中的哪一種,取決于 Java 堆內(nèi)存是否規(guī)整。而 Java 堆內(nèi)存是否規(guī)整,取決于 GC 收集器的算法是"標記-清除",還是"標記-整理"(也稱作"標記-壓縮"),值得注意的是,復制算法內(nèi)存也是規(guī)整的
在創(chuàng)建對象的時候有一個很重要的問題,就是線程安全,因為在實際開發(fā)過程中,創(chuàng)建對象是很頻繁的事情,作為虛擬機來說,必須要保證線程是安全的,通常來講,虛擬機采用兩種方式來保證線程安全:
CAS+失敗重試: CAS 是樂觀鎖的一種實現(xiàn)方式。所謂樂觀鎖就是,每次不加鎖而是假設(shè)沒有沖突而去完成某項操作,如果因為沖突失敗就重試,直到成功為止。虛擬機采用 CAS 配上失敗重試的方式保證更新操作的原子性。
TLAB: 為每一個線程預先在 Eden 區(qū)分配一塊兒內(nèi)存,JVM 在給線程中的對象分配內(nèi)存時,首先在 TLAB 分配,當對象大于 TLAB 中的剩余內(nèi)存或 TLAB 的內(nèi)存已用盡時,再采用上述的 CAS 進行內(nèi)存分配
Step3:初始化零值
內(nèi)存分配完成后,虛擬機需要將分配到的內(nèi)存空間都初始化為零值(不包括對象頭),這一步操作保證了對象的實例字段在 Java 代碼中可以不賦初始值就直接使用,程序能訪問到這些字段的數(shù)據(jù)類型所對應的零值。
Step4:設(shè)置對象頭
初始化零值完成之后,虛擬機要對對象進行必要的設(shè)置,例如這個對象是那個類的實例、如何才能找到類的元數(shù)據(jù)信息、對象的哈希碼、對象的 GC 分代年齡等信息。 這些信息存放在對象頭中。 另外,根據(jù)虛擬機當前運行狀態(tài)的不同,如是否啟用偏向鎖等,對象頭會有不同的設(shè)置方式。
Step5:執(zhí)行 init 方法
在上面工作都完成之后,從虛擬機的視角來看,一個新的對象已經(jīng)產(chǎn)生了,但從 Java 程序的視角來看,對象創(chuàng)建才剛開始,<init> 方法還沒有執(zhí)行,所有的字段都還為零。所以一般來說,執(zhí)行 new 指令之后會接著執(zhí)行 <init> 方法,把對象按照程序員的意愿進行初始化,這樣一個真正可用的對象才算完全產(chǎn)生出來。
?
內(nèi)存布局
在 Hotspot 虛擬機中,對象在內(nèi)存中的布局可以分為 3 塊區(qū)域:對象頭、實例數(shù)據(jù)和對齊填充。
Hotspot 虛擬機的對象頭包括兩部分信息,第一部分用于存儲對象自身的自身運行時數(shù)據(jù)(哈希碼、GC 分代年齡、鎖狀態(tài)標志等等),另一部分是類型指針,即對象指向它的類元數(shù)據(jù)的指針,虛擬機通過這個指針來確定這個對象是那個類的實例。
實例數(shù)據(jù)部分是對象真正存儲的有效信息,也是在程序中所定義的各種類型的字段內(nèi)容。
對齊填充部分不是必然存在的,也沒有什么特別的含義,僅僅起占位作用。?因為 Hotspot 虛擬機的自動內(nèi)存管理系統(tǒng)要求對象起始地址必須是 8 字節(jié)的整數(shù)倍,換句話說就是對象的大小必須是 8 字節(jié)的整數(shù)倍。而對象頭部分正好是 8 字節(jié)的倍數(shù)(1 倍或 2 倍),因此,當對象實例數(shù)據(jù)部分沒有對齊時,就需要通過對齊填充來補全。
?
訪問定位
建立對象就是為了使用對象,我們的 Java 程序通過棧上的 reference 數(shù)據(jù)來操作堆上的具體對象。對象的訪問方式由虛擬機實現(xiàn)而定,目前主流的訪問方式有①使用句柄和②直接指針兩種:
句柄:?如果使用句柄的話,那么 Java 堆中將會劃分出一塊內(nèi)存來作為句柄池,reference 中存儲的就是對象的句柄地址,而句柄中包含了對象實例數(shù)據(jù)與類型數(shù)據(jù)各自的具體地址信息;
直接指針:?如果使用直接指針訪問,那么 Java 堆對象的布局中就必須考慮如何放置訪問類型數(shù)據(jù)的相關(guān)信息,而 reference 中存儲的直接就是對象的地址。
這兩種對象訪問方式各有優(yōu)勢。使用句柄來訪問的最大好處是 reference 中存儲的是穩(wěn)定的句柄地址,在對象被移動時只會改變句柄中的實例數(shù)據(jù)指針,而 reference 本身不需要修改。使用直接指針訪問方式最大的好處就是速度快,它節(jié)省了一次指針定位的時間開銷。
?
總結(jié)
以上是生活随笔為你收集整理的Hotspot虚拟机的对象的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: leetcode198 打家劫舍
- 下一篇: 贪吃蛇源代码111