引用計數(Reference Counting)可作為記憶體管理辦法,也是老代jvm垃圾回收策略之一,原理簡單但是仍有廣泛的引用,如OkHttp,netty等。
回收原理
物件在建立例項的時候會在堆記憶體申請記憶體時給物件引用記為1,當有其他物件新增對此物件的引用持有時,就把改物件的引用計數+1,釋放引用時-1,直至引用計數減至0,該物件的記憶體就會被釋放。
特徵
優點:
- 回收及時:引用數為0,立即回收。如果引用的物件活躍,能夠及時回收的指標就顯的比較重要。
- 卡頓優化:相比較跟蹤式的回收策略,不會被頻繁觸發而引起卡頓
- 域物件:空間作用域只是指定的物件,不會掃描全物件
缺點:
- 執行時效率降低
- 迴圈引用無法釋放
引用執行時效率可以按照應用場景做點優化,如:只關心物件引用0和1的狀態,可以不對1以上的新增引用做加法;延遲引用計數等。
結合以上優缺點,選擇合適的應用場景來判斷是否可以選擇引用計數來實現記憶體管理。
那麼
- 怎麼去實現引用計數呢?
- 需要關注那些細節?
實現
netty作為高效能的IO框架,其記憶體管理部分也採用的引用計數實現。
ByteBuf 是netty資訊傳輸的載體,會被大量建立,所以需要對其做有效的記憶體管理。當然他也是一個具備生命週期的域物件,使用引用計數最為合適不過了。
URL類圖關係如下:
所有的實現類都會實現ReferenceCounted介面。AbstractReferenceCountedByteBuf
private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater =
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
private volatile int refCnt;
protected AbstractReferenceCountedByteBuf(int maxCapacity) {
super(maxCapacity);
refCntUpdater.set(this, 1);
}
複製程式碼
物件持有了refCnt變數用於引用計數的變數,volatile保證多執行緒中可以拿到最新的修改值,但眾所周知,volatile並不能保證變數的安全性,使用AtomicIntegerFieldUpdater(Atomic的子類)樂觀鎖(CAS)來保證執行緒安全,並且儘可能的降低效能損耗。
新增和釋放引用方式
從程式碼中看出新增計數可在例項建立的構造實現。
釋放引用方式:
ByteBuf buffer = ...;
boolean released = buffer.release();
複製程式碼
總結
引用計數的實現主要依託域物件的生命週期,同時需要考慮多執行緒更新引用的場景下的執行緒安全等問題
喜歡的同學關注點贊或關注哦~