JVM-02内存区域与内存溢出异常(中)【hotspot虚拟机对象】
文章目錄
- 思維導圖
- 對象的創建
- 對象的內存布局
- 實例數據(Instance Data)
- 對齊填充(Padding)
- 對象的訪問定位
- 使用句柄(類似間接指針)
- 直接指針訪問 (HotSpot采用這種方式)
- 兩種方式的比較
思維導圖
在 JVM-01自動內存管理機制之Java內存區域與內存溢出異常(上)中我們介紹了 運行時數據區域,這里我們來繼續探討下hotspot虛擬機對象
對象的創建
在語言層面上,創建對象(例如克隆、反序列化)通常僅僅是一個new 關鍵字而己,而在虛擬機中,對象(指普通Java 對象,非數組和Class 對象等) 的創建是一個非常復雜的過程。
虛擬機遇到一條new 指令時,首先將去檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,并且檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果沒有,那必須先執行相應的類加載過程。在類加載檢查通過后,接下來虛擬機將為新生對象分配內存。對象所需內存的大小在類加載完成后便可完全確定,為對象分配空間的任務等同于把一塊確定大小的內存從Java 堆中劃分出來。
假設Java 堆中內存是絕對規整的,就僅僅是把指針向空閑空間那邊挪動一段與對象大小相等的距離,這種分配方式稱為**“指針碰撞**”( Bump the Pointer)。
如果Java 堆中的內存并不是規整的,已使用的內存和空閑的內存相互交錯,虛擬機就必須維護一個列表,記錄哪些內存塊是可用的, 在分配的時候從列表中找到一塊足夠大的空間劃分給對象實例, 并更新列表上的記錄,這種分配方式稱為“空閑列表”( Free List )。
選擇哪種分配方式由Java 堆是否規整決定,而Java 堆是否規整是由所采用的垃圾收集器是否帶有壓縮整理功能決定。因此,在使用Serial、ParNew 等帶Compact(緊湊)過程的收集器時,系統采用的分配算法是指針碰撞,而使用CMS 這種基于Mark-Sweep 算法的收集器時,通常采用空閑列表。
還要考慮內存分配在多線程下同步問題。一種解決辦法是對分配內存空間的動作進行同步處理。實際上虛擬機采用CAS配上失敗重試的方式保證更新操作的原子性:另一種是把內存分配的動作按照線程劃分在不同的空間之中進行,每個線程在Java 堆中預先分配一小塊內存,稱為本地線程分配緩沖( Thread Local Allocation Buffer)。哪個線程要分配內存,就在哪個線程的TLAB 上分配,只有TLAB 用完并分配新的于LAB 時,才需要同步鎖定。
內存分配完成后,虛擬機需要將分配到的內存空間都初始化為零值(不包括對象頭),接下來,虛擬機要對對象進行必要的設置。例如這個對象是哪個類的實例、如何才能找到類的元數信息、對象的哈希碼、對象的GC 分代年齡等信息。這些信息存放在對象的對象頭(Object Header)之中。根據虛擬機當前的運行狀態的不同,如是否啟用偏向鎖等。
對象的內存布局
在HotSpot虛擬機中,對象在內存中存儲的布局可以分為3 塊區域:對象頭( Header )、實例數據(Instance Data)和對齊填充(Padding)。
##對象頭( Header )
Hot Spot 虛擬機的對象頭包括兩部分信息
- 第一部分用于存儲對象自身的運行時數據,如哈希碼(HashCode)、GC分代年齡、鎖狀態標志、線程持有的鎖、偏向線程ID 、偏向時間戳等
- 另外一部分是類型指針,即對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例
實例數據(Instance Data)
數據部分,是對象存儲的真正有效信息,也是在程序代碼中所定義的各種類型字段的內容。包括父類和接口繼承下來的,也包括子類中定義的。這部分的存儲順序會受到虛擬機分配策略參數和字段在Java源代碼中定義順序的影響。從分配策略中知道,相同寬度的字段總是被分配到一起。在這個前提下父類定義的變量會出現在子類之前。
對齊填充(Padding)
對齊填充,不是必須的,只起到地址對齊的作用。HotSpot自動內存管理系統要求對象起始地址必須是8字節的整數倍,也就是對象內存大小必須是8字節的整數倍。因此當對象實例數據沒有對齊時,就需要通過Padding的方式來補全。
對象的訪問定位
Java程序通過棧上的reference數據來操作堆上的具體對象,由于reference類型在Java虛擬機規范中只規定了一個指向對象引用。而沒有規定這個引用應該通過何種方式去定位、訪問堆中的對象的具體位置,它取決于Java虛擬機實現。
目前主要有如下兩種實現方式
使用句柄(類似間接指針)
在Java堆中劃分出一塊內存來作為句柄池,reference中存儲的就是對象的句柄地址,句柄中包含對象實例數據與類型各自具體地址信息
直接指針訪問 (HotSpot采用這種方式)
Java堆中的對象布要考慮如何放置訪問類型數據相關的信息,而reference中存儲的直接就是對象地址
兩種方式的比較
句柄的好處reference中存儲的是穩定的句柄地址,在對象被移動時只會改變句柄中的實例數據指針,而reference本身不需要修改。
直接指針訪問的最大好處就是速度更快,節省一次指針定位時間開銷,因為對象訪問在Java中非常頻繁,這類開銷積少成多也非常可觀。
總結
以上是生活随笔為你收集整理的JVM-02内存区域与内存溢出异常(中)【hotspot虚拟机对象】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 实战SSM_O2O商铺_40【前端展示】
- 下一篇: JVM-03内存区域与内存溢出异常(下)