JVM記憶體管理——總結篇
阿新 • • 發佈:2020-07-07
JVM記憶體管理——總結篇
記憶體劃分及作用
程式計數器
- 執行緒私有、位元組碼行號指示器。
- 執行Java方法,計數器記錄的是位元組碼指令地址;執行本地(Native)方法時,為空。
本地方法棧
與虛擬機器器棧類似,為Native方法服務Java虛擬機器器棧
- 每個方法執行對應一個棧幀,儲存區域性變量表、運算元棧、動態連線、方法出口等資訊
- 區域性變量表:存放編譯期可知的基本資料型別、物件引用、返回值地址
- 區域性變量表以區域性變數槽為單位,long和double佔兩個槽位,其餘一個
- 棧幀中的記憶體大小在編譯期間已經確定
- 執行緒請求的記憶體大於虛擬機器器允許的深度,報錯stackoverflowerror;棧拓展時無法申請足夠記憶體,報錯OutOfMemoryError
Java堆
執行緒共享、唯一目的存放物件例項
方法區
儲存型別資訊、常量、靜態變數、程式碼快取等執行時常量池
編譯期生成的字面量和符號引用
直接記憶體
Java堆中的DirectByteBuffer物件對這塊記憶體直接操作,避免資料在Native和Java堆中來回複製。
常見問題
普通物件的建立過程
- 檢測類是否已被載入
當虛擬機器器遇到 new 指令時,首先先去檢查這個指令的引數是否能在常量池中定位到一個類的符號引用,並且檢查這個符號引用代表的類是否已被載入、解析和初始化過。如果沒有,就執行類載入過程。 - 為物件分配記憶體
類載入完成以後,虛擬機器器就開始為物件分配記憶體,此時所需記憶體的大小就已經確定了 - 為分配空間初始化零值
保證物件沒有賦初始值也可以使用 - 其他設定
設定物件頭資訊,如所屬類、hashcode、gc分代年齡 - 執行init方法
按程式程式碼分配初始值
- 檢測類是否已被載入
Java堆為例項分配記憶體的方式
選擇哪種分配方式由Java堆是否規整決定的,而Java堆記憶體是否規整由垃圾回收器是否帶有空間壓縮整理能力決定的
Serial、ParNew === > 指標碰撞
CMS=== >空閒列表連續空間
使用指標碰撞方式,移動被佔記憶體和可用空間的指標來分配。多執行緒發生記憶體衝突時,利用CAS加失敗重試保證分配;或者本地執行緒分配快取(TLAB)方式分配記憶體非連續空間
維護一張列表,記錄可用空間,分配記憶體更新列表
物件記憶體佈局
- 物件頭
第一部分“Mark Word”:執行時資料,雜湊碼、GC分代年齡、鎖狀態
第二部分:型別指標,指向類的元資料; - 例項資料
- 對其填充(因為物件起始地址必須是8位元組的整倍數)
- 物件頭
物件的兩種訪問定位
棧中的reference資料引用,引用分為“控制程式碼訪問”、“直接指標訪問”兩種
- 控制程式碼訪問
堆中劃分控制程式碼池,控制程式碼中包含物件例項資料和型別資料各自具體地址。優點:物件被移動時,只需改變控制程式碼中例項資料指標 - 直接訪問
直接訪問物件地址。優點:少了一次開銷,訪問速度更快
- 控制程式碼訪問