前言
本篇是java記憶體區域管理系列教程之一
java建立對象的過程,教程全系列内容如下
1.java記憶體區域的劃分
2.如何向java記憶體區域存值
3.如何判斷java記憶體區域中的那些值是無用的
4.如何将java記憶體區域中無用的值清理掉
5.java記憶體區域的垃圾清理器
6.低延時垃圾收集器-Shenandoah
今天我們談談
當java中new一個對象,背後發生了什麼概括說來,就是 先後執行
類加載,配置設定記憶體,初始化零值,設定對象頭,初始化對象,對象引用入棧看完本篇文章,讀者将能夠回答以下問題
1.當java中new一個對象,背後發生了什麼 2.java記憶體配置設定中的指針碰撞和空閑清單配置設定是什麼意思 3.多線程下對象的建立會存在什麼問題碼字不易,贈人玫瑰,手留餘香,點贊的讀者據說都找到了失散已久的妹妹

等待領養的妹妹 :)
正文
我們以最常用的虛拟機HotSpot和最常用的記憶體區域Java堆為例,深入探讨一下HotSpot虛拟機在
Java堆中對象配置設定全過程。
一. 新生對象所屬類的加載檢查
當Java虛拟機遇到一條位元組碼new指令時,首先将去檢查這個指令的參數是否能在常量池中定位到 一個類的符号引用,并且檢查這個符号引用代表的類是否已被加載、解析和初始化過。如果沒有,那必須先執行相應的類加載過程
二. 為新生對象配置設定記憶體
在類加載檢查通過後,接下來虛拟機将為新生對象配置設定記憶體。
對象所需記憶體的大小在類加載完成後便可完全确定,為對象配置設定空間的任務實際上便等同于把一塊确定大小的記憶體塊從Java堆中劃分出來
2.1 如何配置設定到一塊空閑的記憶體
2.1.1 指針碰撞配置設定。假設Java堆中記憶體是絕對規整的,所有被使用過的記憶體都被放在一 邊,空閑的記憶體被放在另一邊,中間放着一個指針作為分界點的訓示器,那所配置設定記憶體就僅僅是把那 個指針向空閑空間方向挪動一段與對象大小相等的距離,這種配置設定方式稱為“
指針碰撞”(Bump The Pointer)。
但如果Java堆中的記憶體并不是規整的,已被使用的記憶體和空閑的記憶體互相交錯在一起,那 就沒有辦法簡單地進行指針碰撞了,虛拟機就必須維護一個清單,記錄上哪些記憶體塊是可用的,在分 配的時候從清單中找到一塊足夠大的空間劃分給對象執行個體,并更新清單上的記錄,這種配置設定方式稱 為“空閑清單”(Free List)
2.1.3 垃圾收集器是否帶有空間壓縮整理決定是上面兩種配置設定方式中的一種選擇哪種配置設定方式由Java堆是否規整決定,而Java堆是否規整又由所采用 的垃圾收集器是否帶有空間壓縮整理(Compact)的能力決定。 是以,當使用Serial、ParNew等帶壓縮整理過程的收集器時,系統采用的配置設定算法是指針碰撞,既簡單又高效; 而當使用CMS這種基于清除(Sweep)算法的收集器時,理論上就隻能采用較為複雜的空閑清單來配置設定記憶體。
三. 新生對象各屬性初始化零值
記憶體配置設定完成之後,虛拟機必須将配置設定到的記憶體空間(但不包括對象頭)都初始化為零值,這步操作保證了對象的執行個體字段 在Java代碼中可以不賦初始值就直接使用,使程式能通路到這些字段的資料類型所對應的零值
四. 設定新生對象的對象頭
接下來,Java虛拟機還要對對象進行必要的設定,例如這個對象是哪個類的執行個體、如何才能找到 類的中繼資料資訊、對象的哈希碼(實際上對象的哈希碼會延後到真正調用Object::hashCode()方法時才 計算)、對象的GC分代年齡等資訊。這些資訊存放在對象的對象頭(Object Header)之中。
五. 為新生對象執行init方法
在上面工作都完成之後,從虛拟機的視角來看,一個新的對象已經産生了。
但是從Java程式的視 角看來,對象建立才剛剛開始——構造函數,即Class檔案中的()方法還沒有執行,所有的字段都 為預設的零值,對象需要的其他資源和狀态資訊也還沒有按照預定的意圖構造好。
一般來說new指令之後會接着執行 ()方法,按照程式員的意願對對象進行初始化,這樣一個真正可用的對象才算完全被構造出來。
六. 将新生對象的引用入棧
如果有類似于Student s =new Student();形式的引用的話,在棧區定義Student類型引用變量s,然後将堆區對象的位址指派給它.
擴充
擴充1:new對象過程案例:
Student類代碼如下
public
Student s = new Student()做了哪些事情?
1、把Student. class檔案加載到記憶體
2、在棧記憶體給s變量開辟一個空間
3、在堆記憶體為學生對象申請一個空間
4、給成員變量進行預設初始化。null,0
5、給成員變量進行顯示初始化。林青霞,27
6、通過構造方法給成員變量進行初始化。劉意,30
7、資料初始化完畢,然後把堆記憶體的位址值指派給棧記憶體的s變量。
擴充2: 建立對象指令重排序問題:
new一個對象的簡單分解動作
- 配置設定對象的記憶體空間
- 初始化對象
- 設定引用指向配置設定的記憶體位址
其中2和3兩步間會發生指令重排序,導緻多線程時,如果在初始化之前通路對象,則會出現問題,單例模式的雙重檢測鎖模式正是會存在這個問題。可以使用volatile來禁止指令重排序解決問題