對象通過new,反射,clone等方式創建完畢後被存儲到堆中,那麼對象是以什麼樣的方式存儲在堆中的?
對象的內存佈局
對象在堆內存的內存佈局主要有三部分,即對象頭,實例數據,對其填充
對象頭
對象頭主要包含兩部分的內容,一個叫運行時元數據(mark word),一個叫類型指針
- 1.運行時元數據:
- 哈希值(hashcode):對象在堆空間中都有一個首地址值,棧空間的引用根據這個地址指向堆中的對象,這就是哈希值起的作用
- GC分代年齡:對象首先是在Eden中創建的,在經過多次GC後,如果沒有被進行回收,就會在survivor中來回移動,其對應的年齡計數器會發生變化,達到閾值後會進入養老區
- 鎖狀態標誌,在同步中判斷該對象是否是鎖
- 線程持有的鎖
- 線程偏向ID
- 偏向時間戳
- 2.類型指針,指向元數據區的代表當前類的class對象,確定該對象所屬的類型
說明:如果對象是數組,則還需要記錄數組的長度
實例數據(Instance Data)
它是對象真正存儲的有效信息,包括程序代碼中定義的各種字段類型(包括從父類繼承下來的和自己本身擁有的字段),注意這裏有一些規則:相同寬度的字段總是被分配在一起,父類中定義的變量會出現在子類之前,因爲父類的加載是優先於子類加載的
對齊填充
沒有特殊含義,僅僅起到佔位符的作用
小結
現在有一個Customer cust = new Custome()
的對象,通過上面的敘述,我們通過一張圖來直觀的瞭解下:
補充:對象訪問方式
在創建好對象後,通過棧幀上的引用訪問堆中的實例,這種訪問方式可以分爲兩種方式:句柄訪問和直接指針(HotSpot採用)
句柄訪問:
直接指針:
和我們介紹內存佈局是一樣的
兩種方式比較:
直接指針效率往往比句柄訪問高,且不需要專門維護句柄池,內存花費小,當對象實例發生移動時,通過指針訪問的引用也要發生變化,而句柄方式的reference則不需要改變