垃圾回收與內存分配——總結篇

垃圾回收與內存分配

一些基礎

  • 對象的四種引用類型java

    • 強引用,內存不足時報錯oom,但不會該類對象
    • 弱引用,當內存不足時纔會回收
    • 軟引用,無論內存是否充足,在gc都會回收
    • 虛引用,任什麼時候候均可以被回收
  • 怎麼判斷對象是否仍在使用?算法

    • 引用計數法
      每一個對象有一個引用計數屬性,當被引用時計數+1,當引用釋放時-1。兩個對象互相循環使用時會致使對象沒法回收
    • 可達性分析——Java默認
      若從GC Roots向下搜索時,走過的路徑稱爲引用鏈。當對象沒有任何引用鏈時表明已不可用。
  • 可做爲GC Roots的對象有哪幾類?緩存

    • 虛擬機棧中引用的對象
    • 方法區類靜態屬性引用的對象
    • 方法區常量引用的對象
    • 本地方法中引用的對象
  • 方法區回收的必要條件安全

    主要回收兩部份內容:廢棄的常量和再也不使用的類型。性價比很低架構

    • 該類全部實例都已被回收
    • 該類對應的類加載器已被回收(很難)
    • 沒法經過反射訪問該類(該類對應的java.lang,Class沒有被引用)
  • 安全點和安全區域併發

    HotSpot沒有爲每一條指令生成OopMap,只在「特定位置」記錄了這些信息(OopMap可理解成附加信息,對棧內的數據進行說明),這些位置稱爲安全點,線程在安全點能夠被肯定,從而肯定GC Roots信息。佈局

    • 安全點特定位置性能

      1. 循環的末尾
      2. 方法返回前
      3. 調用方法的Call以後
      4. 拋出異常的位置
    • 安全區域線程

    安全區域是指一段代碼片中,引用關係不會發生變化,在這個區域任何地方 GC 都是安全的,安全區域能夠看作是安全點的一個擴展code

    • 線程中斷的方式
    1. 主動式:不對線程操做,簡單的設置一個標記位,線程不斷主動輪詢這個標誌位狀態,爲真時,線程在最近的安全點掛起
    2. 被動式:系統直接把全部線程中斷,如發現有的線程不在安全點時,就恢復執行到最近的安全點(不採用)
  • 併發狀況下的可達性變更解決算法(G1和cms)

    • 增量空間算法——CMS
      當出現新的引用對象時,則記錄下該對象,待掃描結束後再以此爲根從新掃描一次
    • 原始快照算法——G1
      當出現刪除引用時,將要刪除的引用記錄下來,待掃描結束後再將該對象爲根從新掃描

垃圾回收算法

  • 標記-清除算法
    標記須要回收的對象或不須要回收的對象,再統一清除。
    缺點:

    1. 效率不穩定,不管標記仍是清除,效率都會隨着對象增多而下降
    2. 內存空間碎片化
  • 標記-複製算法
    把內存空間分爲兩塊,每次只使用其中一塊,當該塊內存不足後,把存活的對象複製到另外一塊,再把原空間一次性清除。
    優勢:沒有內存碎片。
    缺點:內存空間利用率低;當存活對象較多時,效率變低

  • 標記-整理算法
    標記完成後,將存活的對象向一端移動,清除邊界之外的內容
    優勢:內存規整,對象賦值/建立速度快
    缺點:標記、清理效率不高

  • 分代收集

    分代收集是將對象按照存活時間進行分代(新生代和老年代),根據不一樣區域的特色結合前三種算法進行收集

    • 新生代,每次垃圾收集都有大量對象死去,選用標記-複製算法

      新生代複製算法的改進:許多新生代的對象存活時間較短,不須要按照1:1的比例進行復制算法內存分配,可將內存分爲較大的Eden區和兩塊較小的Survivor,回收時將Eden 和 Survivor 中還存活着的對象一次性地複製到另一塊 Survivor 空間上,最後清理掉 Eden 和剛纔用過的 Survivor 空間

    • 老年代,對象存活率較高,沒有其他區域能夠進行分配擔保,使用標記-清除、標記-整理算法

垃圾回收器

  • 新生代

    • Serial
      串行,標記複製算法。客戶端默認新生代收集器
    • ParNew
      並行,標記複製算法;只有ParNew和serial能夠配合CMS使用
    • Parallel Scavenge
      並行,標記複製算法;吞吐量優先收集器,可控制吞吐量(用戶代碼運行時間/總時間)
      1. 最大垃圾收集停頓時間參數[絕對值]
      2. 垃圾回收時間佔總時間比率參數[相對值]
      3. 自適應調節空間參數[布爾值],開啓後可自動調節Eden、Survivor區域大小
  • 老年代

    • Serial Old
      標記整理算法,主要供客戶端模式,若用於服務端:與Parallel Scavenge搭配使用;或者做爲CMS發生失敗後的與預案
    • Parallel Old
      標記整理算法,Parallel Scavenge的老年版
    • CMS
  • CMS

    響應時間優先,標記清除算法。多用於B/S架構的服務端上

    • 四個步驟:
      1. 初始標記:STW,只標記GC Roots直接關聯到的對象
      2. 併發標記:與用戶線程一塊兒遍歷整個對象圖
      3. 從新標記:STW,修整併發標記期間,因用戶線程致使引用變化的部分(增量更新算法實現)
      4. 併發清除:與用戶線程並行,清除已死亡的對象
    • 三個缺點:
      1. 對處理器資源敏感,默認啓動線程數(處理器核心數+3)/4
      2. 產生浮動垃圾。併發標記和清除階段會與用戶線程一塊兒運行,因此須要預留一部分空間供用戶線程使用,當空間不足時,會臨時啓用預案:使用serial old進行標記整理
      3. 標記清除算法致使內存碎片。大對象不夠分配時,觸發full gc耗時。可設置參數,屢次清楚後,在下一次full gc前進行一次整理
  • G1

    面向整個堆空間的Region佈局形式,每一個Region均可以扮演Eden、Survivor或老年代空間。容許設定收集停頓時間

    • 四個步驟:
    1. 初始標記:在minor gc階段只標記gc Roots引用的對象,因此不存在停頓
    2. 併發標記:與用戶線程併發執行,標記完成後處理SATB(原始快照算法)記錄在併發時有變更的對象
    3. 最終標記:STW(短暫),處理併發結束後仍遺留的少許SATB記錄
    4. 篩選回收:STW,對各Region的回收價值與成本進行排序,根據指望停頓時間指定回收計劃。將存活對象複製到空閒Region後,清除原Region

常見問題

  • finalize() 方法何時被調用?它的目的是什麼?
    對象被釋放前被垃圾回收器調用,目的是釋放該對象所持有的資源(對外內存、長鏈接等),且該方法只會被調用一次。(不建議使用)
  • 爲何要有不一樣的引用類型? 因爲gc回收時機不可控,且有時候須要適當的控制對象被回收的時機。好比新建 Person類,每次查詢信息都須要從新構建實例,對象生命週期太短,引發巨大的消耗。聽過軟引用和HashMap的結合能夠構建高速緩存,提高性能。
相關文章
相關標籤/搜索