首頁 > 科技

乾貨!阿里內部強推這份JVM記憶體解析詳解筆記,理論實戰一鍵搞定

2021-07-06 03:04:58

一、物件的例項化

1、創建物件的方式

new最常見的方式變形1 : Xxx的靜態方法變形2 : XxBuilder/XxoxFactory的靜態方法Class的newInstance():反射的方式,只能呼叫空參的構造器,許可權必須是publicConstructor的newInstance(Xxx):反射的方式,可以呼叫空參、帶參的構造器,許可權沒有要求使用clone() :不呼叫任何構造器,當前類需要實現Cloneable介面,實現clone()使用反序列化:從檔案中、從網路中獲取一個物件的二進位制流第三方庫Objenesis2、創建物件的步驟

判斷物件對應的類是否載入、連結、初始化 檢查元空間的常量池中是否有類的符號引用,檢查是否載入連結初始化。 如果是,則生成對應的Class檔案。 如果否,則在雙親委派機制模式下,使用當前類似 【ClassLoader+包名+類名】為Key查詢對應的.class檔案。 如果找到,進行載入,生成對應的Class檔案。 如果找不到,爆出ClassNotFoundException為物件分配記憶體 計算物件佔用大小,堆中分配對應位元組。如果例項變數是引用變數,僅分配4個位元組。 如果記憶體規整:指針碰撞 所有用過的記憶體在一邊,空閒的記憶體在另外一邊,中間放著一個指針作為分界點的指示器,分配記憶體就僅僅是把指針向空閒那邊挪動一段與物件大小相等的距離罷了。如果垃圾收集器選擇的是Serial、ParNew這種基於壓縮演算法的,虛擬機器採用這種分配方式。一般使用帶有compact (整理)過程的收集器時,使用指針碰撞。 如果記憶體不規整:空閒列表 如果記憶體不是規整的,已使用的記憶體和未使用的記憶體相互交錯,那麼虛擬機器將採用的是空閒列表法來為物件分配記憶體。意思是虛擬機器維護了一個列表,記錄上哪些記憶體塊是可用的,在分配的時候從列表中找到一塊足夠大的空間劃分給物件例項,並更新列表上的內容。這種分配方式稱為「空閒列表(Free List)。 說明:選擇哪種分配方式由Java堆是否規整決定,而Java堆是否規整又由所採用的垃圾收集器是否帶有壓縮整理功能決定。 給物件的屬性賦值的操作先後順序: ① 屬性的預設初始化 ② 顯式初始化/程式碼塊中初始化 (誰在前線誰執行) ③ 構造器中初始化處理併發安全問題 CAS ( Compare And Swap )失敗重試、區域加鎖:保證指針更新操作的原子性; TLAB把記憶體分配的動作按照執行緒劃分在不同的空間之中進行,即每個執行緒在Java堆的伊甸園區中預先分配一小塊記憶體,稱為本地執行緒分配緩衝區,(TLAB ,Thread Local Allocation Buffer) 虛擬機器是否使用TLAB,可以通過一XX:+UseTLAB參數來 設定。初始化分配到的空間 記憶體分配結束,虛擬機器將分配到的記憶體空間都初始化為零值。這一步保證了物件的例項欄位在Java程式碼中可以不用賦初始值就可以直接使用,程式能訪問到這些欄位的資料類型所對應的零值。設定物件的物件頭 將物件的所屬類(即類的元資料資訊)、物件的HashCode和物件的GC資訊、鎖資訊等資料儲存在物件的物件頭中。這個過程的具體設定方式取決於JVM實現。執行init方法進行初始化 在Java程式的視角看來,初始化才正式開始。初始化成員變數,執行例項化程式碼塊,呼叫類的構造方法,並把堆內物件的首地址賦值給引用變數。 因此一般來說(由位元組碼中是否跟隨有invokespecial指令所決定),new指令之 後會接著就是執行方法,把物件按照程式設計師的意願進行初始化,這樣一個真正可用的物件才算完全創建出來。載入類元資訊 -》 為物件分配記憶體 -》處理併發問題 -》屬性的預設初始化 -》設定物件頭 -》init方法

二、物件的記憶體佈局

1、物件頭

運行時元資料雜湊值( HashCode )GC分代年齡鎖狀態標誌執行緒持有的鎖偏向執行緒ID偏向時間戳類型指針:指向類元資料的InstanceKlass,確定該物件所屬的類型說明:如果是陣列,還需記錄陣列的長度

2、例項資料

它是物件真正儲存的有效資訊,包括程式程式碼中定義的各種類型的欄位(包括從父類繼承下來的和本身擁有的欄位) 規則:

相同寬度的欄位總被分配在一起父類中定義的變數會出現在子類之前如果CompactFields參數為true(預設為true),子類的窄變數可能插入到父類變數的空隙3、填充

不是必須的,也沒特別含義,僅僅起到佔位符作用

4、小結

三、物件的訪問定位

JVM是如何通過棧幀中的物件引|用訪問到其內部的物件例項的呢?-> 定位,通過棧上reference訪問

物件訪問的主要方式有兩種

控制代碼訪問 缺點:效率較低,需要專門開闢空間儲存指針 優點:比較穩定,當指針修改時,棧幀中的引用不需要改,只改堆中對應的控制代碼池即可。直接指針(HotSpot採用)

四、直接記憶體

不是虛擬機器運行時資料區的一部分,也不是《Java虛擬機器規範》中定義的記憶體區域直接記憶體是Java堆外的、直接向系統申請的記憶體區間

來源於NIO(1.7之後的new I/O),通過存在堆中的DirectByteBuffer操作本地記憶體

I/O讀取檔案NI/O讀取檔案通常,訪問直接記憶體的速度會優於Java堆。即讀寫效能高 因此出於效能考慮,讀寫頻繁的場合可能會考慮使用直接記憶體 Java的NIO庫允許Java程式使用直接記憶體,用於資料緩衝區 也可能導致OutOfMemoryError異常:OutOfMemoryError: Direct buffer memory 由於直接記憶體在Java堆外,因此它的大小不會直接受限於一Xmx指定的最大 堆大小,但是系統記憶體是有限的,Java堆和直接記憶體的總和依然受限於作業系統能給出的最大記憶體。 ==缺點== 分配回收成本較高 不受JVM記憶體回收管理 直接記憶體大小可以通過MaxDirectMemorySize設定 如果不指定,預設與堆的最大值一Xmx參數值一致


IT145.com E-mail:sddin#qq.com