GC的出現解放了程式員需要手動回收記憶體的苦惱,但我們也是要了解GC的,知己知彼,百戰不殆嘛。
常見的GC回收算法主要包括引用計數算法、标記清除算法、複制算法、标記壓縮算法、分代算法以及分區算法。
今天來聊聊引用計數算法。
1原理
顧名思義,此種算法會在每一個對象上記錄這個對象被引用的次數,隻要有任何一個對象引用了次對象,這個對象的計數器就+1,取消對這個對象的引用時,計數器就-1。任何一個時刻,如果該對象的計數器為0,那麼這個對象就是可以回收的。
打個比方:
public static void method() {
A a = new A();
}
public static void main(String[] args) {
method();
}
main函數調用method方法,method方法中new了一個A的對象,指派給局部變量a,此時堆記憶體中的對象A的執行個體的計數器就會+1。當方法結束時,局部變量會随之銷毀,堆記憶體中的對象的計數器就會-1。
2存在的問題
該算法存在兩個問題:
(1)無法處理循環引用的情況。
(2)從上述的原理可知,堆内對象的每一次引用指派和每一次引用清除,都伴随着加減法的操作,會帶來一定的性能開銷。
是以Java沒有使用這種算法來實作GC。
下面來解釋一下第一個問題,循環引用的情況。
即對象A引用對象B,對象B引用對象A。
考慮如下代碼:
class A {
private B b;
public void setB(B b) {
this.b = b;
}
}
class B {
private A a = new A();
public void setA(A a) {
this.a = a;
}
}
public void method() {
A a = new A();
B b = new B();
a.setB(b);
b.setA(a);
}
其記憶體圖示如下
method方法中,執行完兩個set後,method方法結束,圖中兩條紅線引用消失,可以看到,留下兩個對象在堆記憶體中循環引用,但此時已經沒有地方在用他們了,造成記憶體洩漏。兩個對象就淩亂在風中不知所措了。
大家可以關注本人公衆号【Mr羽墨青衫】,會定期推送一些原創技術文章。