JVM系列3 對象創(chuàng)建、布局、訪問和內存分配

參考:
對象創(chuàng)建、布局和訪問:http://www.itdecent.cn/p/ac162726d7de
對象內存分配:http://www.itdecent.cn/p/fa3569127416

一.對象的創(chuàng)建

1.類加載檢查
  • 創(chuàng)建對象的指令的參數(shù)是否能在常量池定位到一個類的符號引用
  • 這個符號引用代表的類是否已被加載、解析和初始化
2.為對象分配內存
  • 對象所需內存大小在類加載完成后已確定,虛擬機將中一塊確定大小的內存劃給對象
  • 根據(jù)堆中是否規(guī)整有兩種內存分配方式
    1)指針碰撞(Bump the pointer)
    堆中內存規(guī)整,以指針作為已用內存和空閑內存的分界點。分配內存即將分界指針向空閑空間移動一段與對象大小相等的距離。適用Serial、ParNew等收集器。
    2)空閑列表(Free List)
    堆中內存不規(guī)整,虛擬機通過維護一張列表確定內存使用情況。分配內存時更新空閑表。適用CMS等基于Mark-Sweep算法的收集器。
  • 解決分配內存時并發(fā)的問題
    1)時間上同步內存分配動作:CAS + 失敗重試
    2)空間上劃分內存分配動作:每個線程在Java堆中預先分配一小塊內存(TLAB 本地線程分配緩沖)。線程只在自己的TLAB上進行內存分配,只有TLAB用完時才進行同步鎖定
  • 對象內存分配策略
    見后文
3.內存空間初始化
  • 虛擬機將對象分配到的內存空間都初始化為零值
  • 保證對象可以不賦初始值就直接使用
4.對象設置

虛擬機對對象進行必要設置,將設置信息存入對象的對象頭,如:

  • 對象屬于的類
  • 對象的哈希碼
  • 對象的GC分代年齡
5.init
  • 進入對象的init方法,將對象按照程序員的意志初始化

二.對象的內存布局

1.對象 = 對象頭(Header) + 實例數(shù)據(jù)(Instance Data) + 對齊填充(Padding)
2.對象頭(Header)

包括數(shù)據(jù)信息類型指針兩部分

  • 數(shù)據(jù)信息存儲對象自身運行時數(shù)據(jù)
    包括 哈希碼/GC分代年齡/鎖狀態(tài)標志/線程持有的鎖/偏向線程ID/偏向時間戳 等
    HotSpot虛擬機對象頭
  • 類型指針指向對象的類元數(shù)據(jù)
    虛擬機據(jù)此確定對象是哪個類的實例
3.實例數(shù)據(jù)(Instance Data)
  • 對象真正存儲的有效信息,即代碼定義的各類字段內容,包括繼承的父類字段
  • 相同寬度的字段分配到一起,默認分配策略為 longs/doubles,ints,shorts/chars,bytes/booleans,oop
4.對齊填充(Padding)
  • 占位符,保證對象的起始地址為8字節(jié)的整數(shù)倍

三.對象的訪問定位

  • 對象的訪問方式取決于虛擬機實現(xiàn),目前主流有使用句柄直接指針(HotSpot)兩種
1.句柄
  • 中劃分一塊內存作為句柄池
  • 中引用存儲的是對象的句柄地址
  • 堆中句柄存儲了對象實例數(shù)據(jù)與類型數(shù)據(jù)的具體地址信息
  • 優(yōu)點:對象移動時只改變句柄池中的實例數(shù)據(jù)指針,不改變引用中的句柄地址
通過句柄訪問對象
2.直接指針
  • 引用中直接存儲對象地址
  • 優(yōu)點:節(jié)省一次指針定位的時間開銷,速度更快
HotSpot通過直接指針訪問對象

四.對象內存分配策略

  • JVM自動內存管理 = 對象內存分配 + 對象內存回收
  • 內存分配規(guī)則細節(jié)取決于當前使用的垃圾收集器組合及虛擬機中與內存相關的參數(shù)設置
  • 下述內存分配策略適用于Serial/Serial Old 和 ParNew/Serial Old 收集器組合
1.對象優(yōu)先分配在新生代Eden區(qū)
  • 當Eden區(qū)空間不足,虛擬機將發(fā)起一次Minor GC
2.大對象直接進入老年代
  • 大對象指需要大量連續(xù)空間的Java對象,如很長的字符串和數(shù)組
  • 大對象(尤其是朝生夕滅的大對象)容易導致虛擬機提前觸發(fā)GC以獲取連續(xù)空間
  • -XX:PretenureSizeThreshold參數(shù)
    設置大于該值的對象直接分配在老年代,以避免新生代大量內存復制操作
3.長期存活對象進入老年代
  • 虛擬機給每個對象定義了一個年齡(Age)計數(shù)器
  • 對象在Eden出生并經過Minor GC進入Survivor后,對象年齡為1
  • 此后對象在Survivor每經歷一次Minor GC,年齡+1
  • 默認對象年齡達到15就將進入老年代,可通過參數(shù) -XX:MaxTenuringThreshold設置
4.動態(tài)對象年齡判定
  • 根據(jù)實際內存狀況需要,虛擬機并不一定要求對象年齡達到MaxTenuringThreshold才晉升老年代
  • 當 Survivor區(qū)中相同年齡所有對象的大小之和>Survivor空間的一半,>=該年齡的對象將直接進入老年代
5.空間分配擔保
  • 空間分配擔保過程
    1)Minor GC前,虛擬機將檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間
    1.1)若大于,則此次Minor GC可確保是安全的
    2)若不大于,則虛擬機根據(jù)HandlePromotionFailure設置值判斷是否允許空間分配擔保失敗
    2.11)若HandlePromotionFailure不允許冒險,則將Minor GC改為Full GC
    3)若HandlePromotionFailure允許冒險,則檢查老年代最大可用連續(xù)空間是否大于歷次晉升老年代對象的平均大小
    3.1)若不大于,則將Minor GC改為Full GC
    4)若大于,則進行一次有風險的Minor GC
  • 空間分配擔保的幾點說明
    1)使用復制收集算法的新生代可能在Minor GC后仍有大量對象存活的情況
    2)此時需老年代進行空間分配擔保,保證Survivor區(qū)無法容納的對象直接進入老年代
    3)老年代進行空間分配擔保的前提,是本身有足夠空間容納這些對象
    4)但在Minor GC前無法確定將移動的對象大小,故取之前每次Minor GC的平均值作參照
    5)如果Minor GC晉升老年代的對象大小 > 之前晉升對象大小平均值,該次空間分配擔保失敗,并重新發(fā)起一次Full GC

清溪非隴水,翻作斷腸流

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • http://www.cnblogs.com/angeldevil/p/3801189.html值得一看 Clas...
    snail_knight閱讀 1,610評論 1 0
  • 第二部分 自動內存管理機制 第二章 java內存異常與內存溢出異常 運行數(shù)據(jù)區(qū)域 程序計數(shù)器:當前線程所執(zhí)行的字節(jié)...
    小明oh閱讀 1,278評論 0 2
  • HostSpot虛擬機運行時內存 程序計數(shù)器——當前線程執(zhí)行字節(jié)碼的行號指示器,如果執(zhí)行Native方法,則計數(shù)器...
    Mars_M閱讀 941評論 0 3
  • JVM內存區(qū)域 JVM將其管理的內存分為若干數(shù)據(jù)區(qū)域,這些數(shù)據(jù)區(qū)域分布情況如下圖所示: 程序計數(shù)器:一塊較小內存區(qū)...
    luoxn28閱讀 773評論 0 0
  • Java和C++之間有一堵由內存動態(tài)分配和垃圾收集技術所圍成的“高墻”,墻外面的人想進來,墻里面的人想出來。 對象...
    胡二囧閱讀 1,329評論 0 4

友情鏈接更多精彩內容