對象由生到死的一些過程
請簡單闡述一下對象的創建過程?
先看一張圖
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年齡)