1. 程式人生 > 其它 >Java記憶體區域部分知識點

Java記憶體區域部分知識點

1、HotSpot虛擬機器物件探祕

1.1、物件的建立

當遇到new指令時候,先檢查指令的引數能否在常量池中得到一個類的符號引用、若沒有,必須先執行相應的類載入過程

 

為新生物件分配記憶體,大小在載入完成後就能確定

 

從java堆中劃分出等記憶體大小的區域,若java堆中的記憶體絕對規整,在空閒與使用過的記憶體中間有一個指標,那麼分配記憶體就是將指標像空閒空間方向挪動與物件大小相等的距離,這個叫做 指標碰撞

若不規整,那麼需要維護一個列表,記錄記憶體塊狀態,找到足夠大的記憶體塊分配給物件例項,這叫 空閒列表

 

 

以上兩種取決於使用哪種的GC(帶壓縮與不帶壓縮)

 

還要考慮物件建立行為是否頻繁,在併發情況下執行緒不安全

解決1、分配空間動作同步處理 保證更新操作的原子性

解決2、按照執行緒規劃在不同空間進行:即每個執行緒預先分配一小塊記憶體(本地執行緒分配緩衝(TLAB))只有快取用完了,分配新緩衝區才需要鎖定

 

 

記憶體分配完成後,虛擬機器必須把分配到的記憶體空間(不包括 物件頭)初始化為零值,若使用了TLAB,此工作可以提前完成

接下來java虛擬機器還需要對物件進行必要設定

 

對於java程式視角看,class檔案的()方法還沒執行 建構函式

 

1.2、物件的記憶體佈局

物件頭、例項資料、對齊填充

 

物件頭包括兩資訊:1、儲存自身執行資料 (HashCode、GC分代年齡、鎖狀態標誌...)

這資料長度在未開啟壓縮指標的虛擬機種分別為32位元與64位元(32位與64位) 稱之為 Mark Word :動態的資料結構

2、型別指標、物件指向它的型別元資料指標,通過該指標可以分辨是哪個類的例項,但並不是所有的虛擬機器必須在物件資料儲存型別指標!

 

如果例項物件是陣列,還有一個記錄陣列長度的指標!

 

 

例項資料:物件真正儲存的有效資訊:各自欄位、父類繼承下來的

 

對其填充:

不是必要存在,沒有特別含義,起到佔位符作用(任何物件必須是8位元組的整數倍)

物件頭精心設計成8的整數倍1~2倍

1.3物件的訪問定位

使用該物件

Java程式通過棧上的 reference操作堆上的具體物件

 

如果使用控制代碼訪問:java劃分一塊記憶體來作為控制代碼池,reference 儲存的是物件的控制代碼地址,控制代碼中包含物件例項資料與型別資料各自具體的地址資訊

使用指標訪問:reference儲存的是物件地址 (速度快,主要使用此方法)

2、OOM異常

1、堆溢位

最常見

常規處理辦法:

記憶體對映分析工具對Dump出來的堆轉儲快照進行分析

1、先確認導致OOM的物件是否必要

分清楚記憶體洩漏還是記憶體溢位

 

記憶體洩露:有不必要的物件,檢視GC回收器為什麼無法回收他們

記憶體溢位:檢查java虛擬機器的堆引數是否有向上調整的空間(Xmx、Xms),再檢查有哪些物件生命週期過長、設計不合理,減少程式執行期的小號

 

2、虛擬機器棧和本地方法棧溢位

1、如果執行緒請求棧深度大於虛擬機器所允許最大深度,丟擲StackOverflowError異常

2、如果虛擬機器允許棧記憶體動態擴充套件、如果擴充套件容量無法申請足夠記憶體,就會OOM

HotSpot虛擬機器不允許動態擴充套件

3、方法區和執行時常量溢位

常量區溢位

String::intern()是一個本地方法,作用是如果字串常量池已經包含一個等於此String物件的字串,則返回String物件的引用,否則將String物件包含的字串新增到常量池中,並且返回此String物件的引用

String.valueOf(i++).intern

 

 

6版本以下常量池在方法區(永久代)的一部分

方法區溢位

一個類要被GC回收,條件很苛刻

8版本,元空間徹底取代永久代

4、本機直接記憶體溢位

直接記憶體(Direct Memory)

容量大小可以通過-XX:MaxDirectMemorySize引數指定,預設與java堆最大值(Xmx)一致

 

直接記憶體導致溢位明顯特徵是在Heap Dump檔案中不會看見有什麼明顯的異常情況,如果發現記憶體溢位後Dump檔案很小,而且使用了DirectMemory( 例如NIO),可以考慮是否直接記憶體溢位