【第四期】GC專題
我在某個技術群裡發現很多人對GC的問題是最多的。確實,由於Java的GC經常會刷存在感(例如佔用大量的CPU時間,full gc時直接失去響應),GC的問題就成了初級的Java程式設計師向中級程式設計師進步過程中不得不面對的一個問題。但是到目前為止,我還沒有在網上看到一個系統的全面的針對Java GC進行總結的文章。我希望在我的公眾號裡專門針對Java GC做一下梳理。
這一期,我們先從Java的分代式GC講起。
垃圾回收(Garbage Collection,GC),本質上是一種自動記憶體管理技術。我們知道在C或者C++語言中,記憶體的申請和釋放是要通過程式設計師手動操作的,而在Java中,則不必這樣做。這是因為Java語言可以自動GC。GC的意思是把不用的記憶體回收掉。
把分配到堆中那些不能通過程式引用的物件稱為非活動物件,也就是死掉的物件,我們稱為“垃圾”。因為我們期望讓記憶體管理變得自動,我們就必須做兩件事情: 1. 找到記憶體空間裡的垃圾;2. 回收垃圾,讓程式設計師能再次利用這部分空間 。只要滿足這兩項功能的程式,就是GC。不一定非得說是在某種語言的runtime裡實現的才叫GC。(這句話的意思是,你完全可以自己為C++寫一個記憶體管理模組,以第三方庫的形式接管C++的記憶體申請和釋放,這也是GC)
Java程式中的各個object之間的引用關係是一個帶有明顯方向性的關係。例如下面的例子:
public class Main { public static void main(String args[]){ try { A a = new A(); a.b = new B(); a.c = new C(); a.b.a = a; a.b.c = a.c; } catch (Exception e) { e.printStackTrace(); } } } class A { public B b; public C c; } class B { public A a; public C c; } class C { Runnable r; }
我們建立了三個物件,如果某一個物件 x 引用了另一個物件 y,那我們就通過一條有向邊把這兩個物件的引用關係表示出來,那麼上述程式就可以表示成:
大家可以看到,我們經過這樣的抽象以後,就把物件之間的引用關係轉換成了一個有向圖。如果我們在原來的邏輯上,再增加一行語句:
a.refB = new B();
這時,從 a 到 b 的引用就不存在了,而是轉向了另外一個 B 的例項。那麼這個 b 就不再被任何物件引用了,就可以標記為垃圾了(實際上,在這個例子中還是有一些細微的差別的,b並不會直接變成垃圾,這是因為從當前程式棧上還有一個指向b物件的隱含的引用。我們現在不必在意這些細節,以後會慢慢細化)
我們已經把物件之間的引用關係轉換成了一個有向圖,那麼我們就可以使用圖演算法來處理記憶體管理領域的一些問題了。