垃圾收集器:引用計數演算法
引用計數演算法作為垃圾收集器最早的演算法,有其優勢,也有其劣勢,雖然現在的JVM都不再採用引用計數演算法進行垃圾回收【例如Sun的Java hotspot採用了火車演算法進行垃圾回收】,但這種演算法也並未被淘汰,在著名的單程式高併發快取Redis中依然採用這種演算法來進行記憶體回收【後緒會以Redis作為例子,說明該演算法】
什麼是引用計數演算法
直白一點,就是對於建立的每一個物件都有一個與之關聯的計數器,這個計數器記錄著該物件被使用的次數,垃圾收集器在進行垃圾回收時,對掃描到的每一個物件判斷一下計數器是否等於0,若等於0,就會釋放該物件佔用的記憶體空間,同時將該物件引用的其他物件的計數器進行減一操作
兩種實現方式
侵入式與非侵入性,引用計數演算法的垃圾收集一般有侵入式與非侵入式兩種,侵入式的實現就是將引用計數器直接根植在物件內部,用C++的思想進行解釋就是,在物件的構造或者拷貝構造中進行加一操作,在物件的析構中進行減一操作,非侵入式恩想就是有一塊單獨的記憶體區域,用作引用計數器
演算法的優點
使用引用計數器,記憶體回收可以穿插在程式的執行中,在程式執行中,當發現某一物件的引用計數器為0時,可以立即對該物件所佔用的記憶體空間進行回收,這種方式可以避免FULL GC時帶來的程式暫停,如果讀過Redis 1.0的原始碼,可以發現Redis中就是在引用計數器為0時,對記憶體進行了回收
演算法的劣勢
採用引用計數器進行垃圾回收,最大的缺點就是不能解決迴圈引用的問題,例如一個父物件持有一個子物件的引用,子物件也持有父物件的引用,這種情況下,父子物件將一直存在於JVM的堆中,無法進行回收,程式碼示例如下所示(引用計數器無法對a與b物件進行回收):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
class A
{ private B
b; public B
getB() { return b; } public void setB(B
b) { this .b
= b; } } class B
{ private A
a; public A
getA() { return a; } public void setA(A
a) { this .a
= a; } } public class Test
{ public static void main(String[]
args) { A
a = new A(); B
b = new B(); a.setB(b); b.setA(a); } } |
如下是Redis 1.0通過使用引用計數器對記憶體進行回收的
1
2
3
4
5
6
7
|
typedef struct redisObject
{ unsigned
type:4; unsigned
encoding:4; unsigned
lru:REDIS_LRU_BITS; /*
lru time (relative to server.lruclock) */ int refcount; //引用計數器 void *ptr; //指向實際的物件空間 }
robj; |
Redis中所有的操作,操作的都是robj這個結構體,在這個結構中存放著物件的引用計數器refcount,如下是建立物件的程式碼,在這個建立物件的過程中,將引用計數器置為1
1
2
3
4
5
6
7
8
9
10
11
|
robj
*createObject( int type, void *ptr)
{ robj
*o = zmalloc( sizeof (*o)); o->type
= type; o->encoding
= REDIS_ENCODING_RAW; o->ptr
= ptr; //建立時將引用計數器初始為1 o->refcount
= 1; /*
Set the LRU to the current lruclock (minutes resolution). */ o->lru
= LRU_CLOCK(); return o; } |
以下操作是對引用計數器進行+1操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
robj
*createStringObjectFromLongLong( long long value)
{ robj
*o; if (value
>= 0 && value < REDIS_SHARED_INTEGERS) { //對共享池中常量物件的引用計數+1 incrRefCount(shared.integers[value]); o
= shared.integers[value]; } else { if (value
>= LONG_MIN && value <= LONG_MAX) { o
= createObject(REDIS_STRING, NULL); o->encoding
= REDIS_ENCODING_INT; o->ptr
= ( void *)(( long )value); } else { o
= createObject(REDIS_STRING,sdsfromlonglong(value)); } } return o; } |
Redis中有一個共享池,共享池中的變數,一般不會輕易釋放,大部份物件都可以對這部份常量進行共享,共享一次,對應物件robj中的引用計數器進行一次+1操作
以下是進行-1操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
void decrRefCount(robj
*o) { if (o->refcount
<= 0) redisPanic( "decrRefCount
against refcount <= 0" ); if (o->refcount
== 1) { switch (o->type)
{ case REDIS_STRING:
freeStringObject(o); break ; case REDIS_LIST:
freeListObject(o); break ; case REDIS_SET:
freeSetObject(o); break ; case REDIS_ZSET:
freeZsetObject(o); break ; case REDIS_HASH:
freeHashObject(o); break ; default :
redisPanic( "Unknown
object type" ); break ; } zfree(o); } else { o->refcount--; } } |
從上面的程式碼中可以看出,對物件的引用計數器進行-1操作時,如果物件的引用計數器變為0時,會呼叫相應型別的釋放函式,釋放物件的記憶體空間,如果物件的引用計數器的值大於1時,直接對物件的引用計數器進行減1操作,然後返回
從上面的程式碼可以看出,Redis中通過對物件的引用計數器進行減1操作,可以實現在程式執行過程中,回收物件所佔用的記憶體空間,當然Redis中還有LRU演算法,實現記憶體淘汰策略,待以後再分析
Redis 1.0原始碼註解:https://github.com/zwjlpeng/Redis_Deep_Read
http://www.cnblogs.com/WJ5888/p/4359783.html
相關文章
- 垃圾回收演算法:引用計數法演算法
- 十、jvm垃圾回收演算法、垃圾收集器、引用你真的瞭解麼?JVM演算法
- 垃圾回收的引用計數器演算法詳解演算法
- Java虛擬機器-GC垃圾回收演算法-引用計數法Java虛擬機GC演算法
- PHP的垃圾回收機制-引用計數PHP
- 垃圾收集器與記憶體分配策略_hotspot垃圾收集演算法實現和垃圾收集器記憶體HotSpot演算法
- 垃圾收集器
- 垃圾收集器與記憶體分配策略_垃圾收集演算法記憶體演算法
- jvm系列(三)GC演算法 垃圾收集器JVMGC演算法
- JDK 15中Z垃圾收集器演算法 - JesúsNavarreteJDK演算法
- 垃圾收集器學習
- JVM垃圾收集器(八)JVM
- JVM垃圾收集器總結JVM
- JVM垃圾收集器專題JVM
- JVM 經典垃圾收集器JVM
- java幾種垃圾收集方法和垃圾收集器Java
- Java G1 垃圾收集器Java
- GC 分代回收 - 垃圾收集器GC
- 13.G1垃圾收集器
- JVM(五)-垃圾收集器入門JVM
- 垃圾回收(四)【弱引用】
- JVM調優:HotSpot JVM垃圾收集器JVMHotSpot
- 深入理解 JVM 之 垃圾收集器JVM
- 10. 系統分析垃圾收集器
- 面試官:談談你對JVM垃圾收集器演算法的瞭解面試JVM演算法
- JVM虛擬機器-垃圾回收機制與垃圾收集器概述JVM虛擬機
- Java虛擬機器04——垃圾收集器Java虛擬機
- 深入理解JVM,7種垃圾收集器JVM
- JVM——垃圾收集器與記憶體分配JVM記憶體
- HotSpot的7種垃圾收集器組合HotSpot
- 垃圾收集器與記憶體分配策略記憶體
- JVM學習筆記(4)---垃圾收集器JVM筆記
- swift自動引用計數Swift
- Java新的Z垃圾收集器ZGC介紹JavaGC
- JVM垃圾收集器基準報告 – Ionuț BaloșinJVM
- JVM 垃圾收集器與記憶體分配策略JVM記憶體
- java8預設使用的垃圾收集器Java
- 編寫你的第一個垃圾收集器
- Bronze垃圾收集器是否使 Rust 更易於使用?Rust