jvm-第三節jvm中的對象及引用

# jvm中的對象以及引用

問題

這篇文章主要探討的幾個問題:

  1. jvm中對象創建過程

  2. 對象的內存佈局

  3. 對象的訪問方式

  4. 如何判斷對象是否存活

  5. 對象分配策略

  6. 四種引用的區別

jvm中對象的創建過程

  1. 檢查加載:檢查指令是否在一個常量池中定位到一個類的符號引用(一組符號描述所引用的目標),檢查類是否加載解析初始化
  2. 分配內存:從jvm的堆中劃分一塊內存,這個劃分有倆種情況,分別是指針碰撞和空閒列表
    1. 1687227244871指針碰撞:如果jvm堆中的空間是規整的,空閒一邊,用過的一邊,中間放一個指針,那麼分配內存僅僅是吧指針挪動與對象大小等距距離;具體如圖
    2. 1687227357553空閒列表:如果jvm堆種地空間不規整,那就需要維護一個空閒列表,分配時在空閒列表上找一塊足夠大的空間分配給對象,然後維護列表,具體如圖
    3. 分配內存的併發安全問題:在多線程情況下分配內存,創建對象,是一個極其頻繁的爲題,t1線程創建對象分配內存修改指針沒結束,t2就進來使用原指針分配內存,針對這個問題有倆種解決方式,一種是CAS,一種是分配緩衝TLAB
      1. 1687228415617CAS:通過cas操作,如果成功就分配內存,如圖
      2. TLAB:是一種基於線程本地緩存方式,通過cas分配內存,每次分配時現在自己的TLAB中找,如果沒有空間了就採用其他分配方式,比如直接分配;
      3. 分配緩衝:預先獲取一塊連續的內存,然後將緩衝池分成多塊,每次分配對象就從緩衝池中拿一塊進行分配;如果沒有空間了,則會使用其他分方式,比如直接分配;
  3. 初始化零值:被分配內存空間的對象都要初始化零值,int =0 boolean =false這一步的意義是保證java對象在不賦值初始化的情況下可以使用
  4. 設置對象頭:jvm對對象的必要設置,包括這個對象是哪個類的實例,如何找到類的元數據,對象hash ,gc,等信息,都在對象頭中;具體看下面
  5. 初始化對象:對於jvm來說,一個對象已經創建完了;

對象的內存佈局

1687230338210

  1. 在hotspot中對象的佈局分爲三類。分別是對象頭,實例數據,對齊填充,分別介紹一下
    1. 對象頭:存儲的是運行的數據它包含三部分,存儲對運行數據(markWord),類型指針,若是數組類型,有記錄數組長度的記錄
      1. 存儲對象自身運行時數據包含 haah,gc分代年齡,鎖標識,鎖類型,偏向線程id,偏向時間戳
    2. 實例數據
    3. 對齊填充

對象的訪問方式

  1. 句柄:如果採用句柄訪問,jvm堆中會劃分一塊區域做句柄池,reference(在對象頭中)中村的是句柄的地址,句柄中存的是對象實例和類型數據的地址
  2. 直接指針:reference中存的就是對象地址
  3. 對比:直接指針訪問塊,但是對象變更速度太快,所以一直修改開銷大,句柄要二次訪問慢,但是對面變更是句柄維護,reference中不需要修改。

判斷對象是否存活的方式

  1. 堆中存放着幾乎所有的對象,gc回收時要判斷這些對象是否存活,如何判斷存活,常見的有引用計數法,可達性分析

    1. 引用計數法:在對象中添加引用計數器,每當有一個地方引用就+1,失效時就-1,存在相互引用情況要引入額外基質處理,影響效率;
    2. 可達性分析:通過gc root作爲起點,從這個節點開始向下搜素,沿途路徑叫引用鏈,當一個對象到gc root沒有任何引用鏈,則證明這對象不可用;
      1. 常見的gc root如下
        1. 虛擬機棧中引用的對象1687247376762
        2. 本地方法棧中引用的對象1687247438766
        3. 方法區靜態屬性引用的對象1687247487481
        4. jni引用的對象1687247517021
        5. 虛擬機內部引用的對象,如常量池引用的對象1687247604184
        6. jvm引用類型,軟弱虛引用
        7. 根集合中的對象,在gc前預設好的對象
  2. Finalize 方法 (沒啥用)

    即使通過可達性分析判斷不可達的對象,也不是“非死不可”,它還會處於“緩刑”階段,真正要宣告一個對象死亡,需要經過兩次標記過程,一次是 沒有找到與 GCRoots 的引用鏈,它將被第一次標記。隨後進行一次篩選(如果對象覆蓋了 finalize),我們可以在 finalize 中去拯救。

四種對象引用以及區別

  1. 強: Object obj = new Object(); // 強引用
  2. 軟:SoftReference sr = new SoftReference<>(new Object()); // 軟引用
  3. 弱: WeakReference wr = new WeakReference<>(new Object()); // 弱引用
  4. 虛: WeakReference wr = new WeakReference<>(new Object()); // 弱引用
  5. 他們之前的區別:那gc爲例,
    1. 強引用-obj存在就不會回收
    2. 軟引用-當內存不足時,會被回收
    3. 弱引用-下一次gc時就會被回收
    4. 虛引用-每逢gc必然回收,放入特殊隊列

對象分配策略

  1. 除了常見的分配在堆中,還會分配到棧中,既逃逸下面詳細說

  2. 對象的分配

    1. 棧上分配

      1. 首先說逃逸分析,分爲倆種,一種是線程逃逸,既被多個線程訪問的對象,另一種是方法逃逸,既被其他方法引用
      2. 如果對象沒有逃逸,則在棧上分配對象,這樣就會避免對象創建和回收;
      3. 舉個例子,myobject屬於不可逃逸,jvm在棧上分配1687250594911
    2. 堆中分配:

      1. 對象優先在堆中的eden:大多數情況下對象在eden上分配,空間不夠就minor gc,這裏會涉及道空間分配擔保機制;

      2. 大對象直接進入老年代

      3. 長期存活的進入老年代:每一個對象有一個年齡計數器(在對象頭的運行時數據區中的gc年齡),達到一定閾值就會進入老年代

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章