2019-08-18

???????????????? 創(chuàng)建對(duì)象的過(guò)程


一個(gè)畫(huà)不完的圓

①類(lèi)加載檢查: JVM將類(lèi)加載過(guò)程分為五個(gè)步驟:

1) 裝載:查找并加載類(lèi)的二進(jìn)制數(shù)據(jù) ;

2)? 驗(yàn)證:確保被加載類(lèi)的正確性;

3)準(zhǔn)備:為類(lèi)的靜態(tài)變量分配內(nèi)存,并將其初始化為默認(rèn)值;

4)解析:虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用的過(guò)程;

符號(hào)引用(Symbolic References):符號(hào)引用以一組符號(hào)來(lái)描述所引用的目標(biāo),符號(hào)可以是任何形式的字面量,只要使用時(shí)能無(wú)歧義地定位到目標(biāo)即可。

直接引用(Direct References):直接引用可以是直接指向目標(biāo)的指針、相對(duì)偏移量或是一個(gè)能間接定位到目標(biāo)的句柄。如果有了直接引用,那么引用的目標(biāo)一定是已經(jīng)存在于內(nèi)存中。

5) 初始化:為類(lèi)的靜態(tài)變量賦予正確的初始值。

比如private static int a = 10,它的執(zhí)行過(guò)程是這樣的,首先字節(jié)碼文件被加載到內(nèi)存后,先進(jìn)行驗(yàn)證這一步驟,驗(yàn)證通過(guò)后準(zhǔn)備階段,給a分配內(nèi)存,因?yàn)樽兞縜是static的,所以此時(shí)a等于int類(lèi)型的默認(rèn)初始值0,即a=0,然后解析,到初始化這一步驟時(shí),才把a(bǔ)的真正的值10賦給a,此時(shí)a=10。靜態(tài)變量是在類(lèi)裝載時(shí)初始化的,因此在產(chǎn)生對(duì)象前就初始化了,這也就是可以使用類(lèi)名訪問(wèn)靜態(tài)變量的原因。

②分配內(nèi)存: 在類(lèi)加載檢查通過(guò)后,接下來(lái)虛擬機(jī)將為新生對(duì)象分配內(nèi)存。對(duì)象所需的內(nèi)存大小在類(lèi)加載完成后便可確定,為對(duì)象分配空間的任務(wù)等同于把一塊確定大小的內(nèi)存從 Java 堆中劃分出來(lái)。分配方式有 “指針碰撞” 和 “空閑列表” 兩種,選擇那種分配方式由 Java 堆是否規(guī)整決定,而Java堆是否規(guī)整又由所采用的垃圾收集器是否帶有壓縮整理功能決定。

內(nèi)存分配并發(fā)問(wèn)題:在創(chuàng)建對(duì)象的時(shí)候有一個(gè)很重要的問(wèn)題,就是線程安全,因?yàn)樵趯?shí)際開(kāi)發(fā)過(guò)程中,創(chuàng)建對(duì)象是很頻繁的事情,作為虛擬機(jī)來(lái)說(shuō),必須要保證線程是安全的,通常來(lái)講,虛擬機(jī)采用兩種方式來(lái)保證線程安全:

?1)CAS+失敗重試CAS 是樂(lè)觀鎖的一種實(shí)現(xiàn)方式。所謂樂(lè)觀鎖就是,每次不加鎖而是假設(shè)沒(méi)有沖突而去完成某項(xiàng)操作,如果因?yàn)闆_突失敗就重試,直到成功為止。虛擬機(jī)采用 CAS 配上失敗重試的方式保證更新操作的原子性。

2)TLAB為每一個(gè)線程預(yù)先在 Eden 區(qū)分配一塊兒內(nèi)存,JVM 在給線程中的對(duì)象分配內(nèi)存時(shí),首先在 TLAB 分配,當(dāng)對(duì)象大于 TLAB 中的剩余內(nèi)存或 TLAB 的內(nèi)存已用盡時(shí),再采用上述的 CAS 進(jìn)行內(nèi)存分配

③初始化零值: 內(nèi)存分配完成后,虛擬機(jī)需要將分配到的內(nèi)存空間都初始化為零值(不包括對(duì)象頭),這一步操作保證了對(duì)象的實(shí)例字段在 Java 代碼中可以不賦初始值就直接使用,程序能訪問(wèn)到這些字段的數(shù)據(jù)類(lèi)型所對(duì)應(yīng)的零值。

④設(shè)置對(duì)象頭: 初始化零值完成之后,虛擬機(jī)要對(duì)對(duì)象進(jìn)行必要的設(shè)置,例如這個(gè)對(duì)象是那個(gè)類(lèi)的實(shí)例、如何才能找到類(lèi)的元數(shù)據(jù)信息、對(duì)象的哈希嗎、對(duì)象的 GC 分代年齡等信息。 這些信息存放在對(duì)象頭中。 另外,根據(jù)虛擬機(jī)當(dāng)前運(yùn)行狀態(tài)的不同,如是否啟用偏向鎖等,對(duì)象頭會(huì)有不同的設(shè)置方式。

⑤執(zhí)行 init 方法: 在上面工作都完成之后,從虛擬機(jī)的視角來(lái)看,一個(gè)新的對(duì)象已經(jīng)產(chǎn)生了,但從 Java 程序的視角來(lái)看,對(duì)象創(chuàng)建才剛開(kāi)始,<init> 方法還沒(méi)有執(zhí)行,所有的字段都還為零。所以一般來(lái)說(shuō),執(zhí)行 new 指令之后會(huì)接著執(zhí)行 <init>方法,把對(duì)象按照程序員的意愿進(jìn)行初始化,這樣一個(gè)真正可用的對(duì)象才算完全產(chǎn)生出來(lái)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第二部分 自動(dòng)內(nèi)存管理機(jī)制 第二章 java內(nèi)存異常與內(nèi)存溢出異常 運(yùn)行數(shù)據(jù)區(qū)域 程序計(jì)數(shù)器:當(dāng)前線程所執(zhí)行的字節(jié)...
    小明oh閱讀 1,298評(píng)論 0 2
  • 一 、java虛擬機(jī)底層結(jié)構(gòu)詳解 我們知道,一個(gè)JVM實(shí)例的行為不光是它自己的事,還涉及到它的子系統(tǒng)、存儲(chǔ)區(qū)域、...
    葡萄喃喃囈語(yǔ)閱讀 1,584評(píng)論 0 4
  • 《深入理解Java虛擬機(jī)》筆記_第一遍 先取看完這本書(shū)(JVM)后必須掌握的部分。 第一部分 走近 Java 從傳...
    xiaogmail閱讀 5,480評(píng)論 1 34
  • JVM有哪些分區(qū)?程序計(jì)數(shù)器,java虛擬機(jī)棧,本地方法棧,堆,方法區(qū)。 java棧中存放的是一個(gè)個(gè)棧幀,每一個(gè)棧...
    irckwk1閱讀 837評(píng)論 0 0
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,680評(píng)論 1 32

友情鏈接更多精彩內(nèi)容