請簡單闡述一下物件的建立過程?
先看一張圖
main方法中建立了兩個物件執行過程在右邊位元組碼中展示完全一致new、dup、invokespecial、astore四個步驟
1、new,虛擬機器指令為物件分配記憶體並在棧頂壓入了指向這段記憶體的地址供後續操作來呼叫
2、dup,其實就是一個複製操作,其作用是把棧頂的內容複製一份再壓入棧。jvm為什麼要這麼做呢
這完全是jvm自己編譯優化的做法,再後續操作之前虛擬機器自己會呼叫一次。我們都知道物件都有一個this的關鍵字指向物件本身,this是什麼時候賦值的呢,就是這個時候
至於另一個引用當然是賦值給方法中的變數了
3、invokespecial,該過程是對例項物件進行初始化,第一步分配記憶體後物件內的例項變數都是初始值,在該步驟才會初始化物件內的例項變數
4、astore,方法內的變數指向記憶體中的物件
如何定位一個物件
1、直接定位也就是指標定位(hotspot使用方式),直接定位到例項物件記憶體地址。這種方式的好處是速度快(對比控制程式碼訪問,減少一次指標開銷);缺點是GC的時候需要改變指標的指向
2、控制程式碼定位,在堆中劃分出一塊記憶體區域作為控制程式碼池,變數都指向控制程式碼池內的地址,再控制程式碼池內指向例項物件以及Class。好處是在GC的時候只需要改變控制程式碼池中的地址。缺點就是查詢慢,畢竟多了一次指標開銷
物件在記憶體中的佈局
總的來說物件在記憶體中佈局總共分為三部分物件頭(mark World、klass pointer)、例項資料、對齊,見下圖示記部分
1、mark world:鎖資訊、hashCode、gc資訊
通過上圖對比,synchronized加鎖之後,我們可以明顯看到markword內資訊發生變化
gc資訊包含gc年齡、顏色標記等資訊
2、klass pointer:我這裡關閉了指標壓縮所以看到的klass pointer 是8個位元組,jdk8預設是開啟指標壓縮的在記憶體小於32G的時候klass pointer是4個位元組。這部分內容主要是指向當前物件的型別也就是class物件
3、例項資料:這部分主要看類中到底有哪些成員變數,如上圖有成員變數int,所以佔4個位元組
4、對齊填充:這部分是可有可無的,物件的大小是8的整數倍,如果無法被8整除,就需要補充對齊,如上述物件需要補充4位元組對齊
物件是在記憶體中是如何分配的
引用一張大神的圖:
簡單來說物件分配的流程 (當然其中有很多細節,物件分配也與使用的gc有關)
1、判斷物件是否可以在棧上分配(逃逸分析、標量替換)
2、判斷物件的大小如果過大直接分配到老年代(-XX:PretenureSizeThreshold)
3、否則分配到eden區,經過gc後,存活的物件移動到 from Survivor區;再次經過gc存活的物件移動到To survivor區 同時兩個survivor互換身份(eden、survivor比例引數 -XX:SurvivorRatio)
4、gc年齡達到閾值,物件移動到老年代(-XX:MaxTenuringThreshold指定移動到老年代gc年齡)