JVM學習:對象的創建和內存分配


1、對象的創建

  java是面向對象的語言,因此對象的創建無時無刻都存在。在語言層面,使用new關鍵字即可創建出一個對象。但是在虛擬機中,對象創建的創建過程則是比較復雜的。

  首先,虛擬機運到new指令時,會去常量池檢查是否存在new指令中包含的參數,比如new People(),則虛擬機首先會去常量池中檢查是否有People這個類的符號引用,並且檢查這個類是否已經被加載了,如果沒有則會執行類加載過程。

  在類加載檢查過后,接下來為對象分配內存當然是在java堆中分配,並且對象所需要分配的多大內存在類加載過程中就已經確定了。為對象分配內存的方式根據java堆是否規整分為兩個方法:1、指針碰撞(Bump the Pointer),2、空閑列表(Free List)。指針碰撞:如果java堆是規整的,即所有用過的內存放在一邊,沒有用過的內存放在另外一邊,並且有一個指針指向分界點,在需要為新生對象分配內存的時候,只需要移動指針畫出一塊內存分配和新生對象即可;空閑列表:當java堆不是規整的,意思就是使用的內存和空閑內存交錯在一起,這時候需要一張列表來記錄哪些內存可使用,在需要為新生對象分配內存的時候,在這個列表中尋找一塊大小合適的內存分配給它即可。而java堆是否規整和垃圾收集器是否帶有壓縮整理功能有關。

  在為新生對象分配內存的時候,同時還需要考慮線程安全問題。因為在並發的情況下內存分配並不是線程安全的。有兩種方案解決這個線程安全問題,1、為分配內存空間的動作進行同步處理;2、為每個線程預先分配一小塊內存,稱為本地線程分配緩存(Thread Local Allocation Buffer, TLAB),哪個線程需要分配內存,就在哪個線程的TLAB上分配。

  內存分配后,虛擬機需要將每個對象分配到的內存初始化為0值(不包括對象頭),這也就是為什么實例字段可以不用初始化,直接為0的原因。

  接來下,虛擬機對對象進行必要的設置,例如這個對象屬於哪個類的實例,如何找到類的元數據信息。對象的哈希嗎、對象的GC年代等信息,這些信息都存放在對象頭之中。

  執行完上面工作之后,所有的字段都為0,接着執行<init>指令,把對象按照程序員的指令進行初始化,這樣一個對象就完整的創建出來。

 

2、對象的內存布局

  對象在內存的存儲布局中包括:對象頭、實例數據、對齊填充

  對象頭(Header):包含兩部分信息。1、存儲對象自身的運行時數據,比如哈希碼、GC分代年齡等;2、類型指針:通過這個指針確定這個對象屬於哪個類。

  實例數據(Instance Data):存儲代碼中定義的各種類型的字段內容。

  對齊填充(Padding):這部分信息沒有任何意義,僅僅是為了使得對象占的內存大小為8字節的整數倍。

 

3、對象的訪問定位

  創建對象是為了使用對象,java程序需要通過棧上的reference數據來操作棧上的具體對象。目前主流的訪問對象方式有使用句柄和直接指針兩種。1、使用句柄方式:會在java堆中創建一個句柄池,reference指向的這塊句柄池,句柄池中包括兩個指針,其中一個指針指向對象實例數據,另外一個指針指向對象的類型數據。2、使用指針的方式:reference存儲的直接就是對象的地址。

  兩種方式各有各的特點,如果使用句柄方式的話,最大的好處是reference存放的是穩定的句柄地址,在對象移動時只會改變句柄中的實例數據指針,而reference本身不需要修改。使用指針的方式優勢則是速度快,並且省去了一次指針定位的開銷。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM