JVM垃圾回收之GC算法

相關術語翻譯說明:

Mark,標記;

Sweep,清除;

Compact,整理; 也有人翻譯為壓縮,譯者認為GC時不存在壓縮這回事。

Copy,複製; copy 用作名詞時一般翻譯為拷貝/副本,用作動詞時翻譯為複製。

本篇文章主要介紹GC回收算法概念

總體而言垃圾收集器都專注於兩件事

  • 查找所有存活對象

  • 拋棄其他的部分,即死對象,不在使用對象。 第一步,記錄所有存活的對象,在垃圾收集中有一個做,標記的過程專門幹這件事。

I.標記可達對象(Marking Reachable Objects)

下示意圖對此作出最好的解釋

JVM垃圾回收之GC算法

首先,有一些特定的對象被指定為 Garbage Collection Roots(GC根元素)。包括:

  • 當前正在執行的方法裡的局部變量和輸入參數

  • 活動線程(Active threads)

  • 內存中所有類的靜態字段(static field)

  • JNI引用

其次, GC遍歷(traverses)內存中整體的對象關係圖(object graph),從GC根元素開始掃描, 到直接引用,以及其他對象(通過對象的屬性域)。所有GC訪問到的對象都被標記(marked)為存活對象。

存活對象在上圖中用藍色表示。標記階段完成後, 所有存活對象都被標記了。而其他對象(上圖中灰色的數據結構)就是從GC根元素不可達的, 也就是說程序不能再使用這些不可達的對象(unreachable object)。這樣的對象被認為是垃圾, GC會在接下來的階段中清除他們。

在標記階段有幾個需要注意的點:

在標記的時候,會暫停所有的應用線程,以遍歷所有對象的引用關係,因為如果不暫停就無法跟蹤一直變化的引用關係。此階段暫停的時間, 與堆內存大小,對象的總數沒有直接關係, 而是由存活對象(alive objects)的數量來決定。所以增加堆內存的大小並不會直接影響標記階段佔用的時間。

II.刪除不可達對象(Removing Unused Objects)

各種GC算法在刪除不可達對象時略有不同, 但總體可分為三類: 清除(sweeping)、整理(compacting)和複製(copying)。

  • Sweep(清除) Mark and Sweep(標記-清除) 算法的概念非常簡單: 直接忽略所有的垃圾。也就是說在標記階段完成後, 所有不可達對象佔用的內存空間, 都被認為是空閒的, 因此可以用來分配新對象。

這種算法需要使用 空閒表(free-list),來記錄所有的空閒區域, 以及每個區域的大小。維護空閒表增加了對象分配時的開銷。此外還存在另一個弱點 :明明還有很多空閒內存, 卻可能沒有一個區域的大小能夠存放需要分配的對象, 從而導致分配失敗(在Java 中就是 OutOfMemoryError)。問題的原因呢?就是碎片太多,如圖示

JVM垃圾回收之GC算法

  • Compact(整理)

標記-清除-整理算法(Mark-Sweep-Compact), 將所有被標記的對象(存活對象), 遷移到內存空間的起始處, 消除了標記-清除算法的缺點。 相應的缺點就是GC暫停時間會增加, 因為需要將所有對象複製到另一個地方, 然後修改指向這些對象的引用。此算法的優勢也很明顯, 碎片整理之後, 分配新對象就很簡單, 只需要通過指針碰撞(pointer bumping)即可。使用這種算法, 內存空間剩餘的容量一直是清楚的, 不會再導致內存碎片問題。

JVM垃圾回收之GC算法

  • Copy(複製) 標記-複製算法(Mark and Copy) 和 標記-整理算法(Mark and Compact) 十分相似: 兩者都會移動所有存活的對象。區別在於, 標記-複製算法是將內存移動到另外一個空間: 存活區。標記-複製方法的優點在於: 標記和複製可以同時進行。缺點則是需要一個額外的內存區間, 來存放所有的存活對象。

JVM垃圾回收之GC算法


分享到:


相關文章: