Netty分布式獲取異線程釋放對象源碼剖析
前文傳送門:異線程下回收對象
獲取異線程釋放對象
上一小節(jié)分析了異線程回收對象, 原理是通過與stack關聯(lián)的WeakOrderQueue進行回收
如果對象經(jīng)過異線程回收之后, 當前線程需要取出對象進行二次利用, 如果當前stack中為空, 則會通過當前stack關聯(lián)的WeakOrderQueue進行取出, 這也是這一小寫要分析的, 獲取異線程釋放的對象
在介紹之前我們首先看Stack類中的兩個屬性
private WeakOrderQueue cursor, prev; private volatile WeakOrderQueue head;
這里都是指向WeakOrderQueue的指針, 其中head我們上一小節(jié)分析過, 指向最近創(chuàng)建的和stack關聯(lián)WeakOrderQueue, 也就是頭結點
cursor代表的是尋找的當前WeakOrderQueue, pre則是cursor上一個節(jié)點, 如圖所示:
8-7-1
我們從獲取對象的入口方法, handle的get開始分析
public final T get() { if (maxCapacityPerThread == 0) { return newObject((Handle<T>) NOOP_HANDLE); } Stack<T> stack = threadLocal.get(); DefaultHandle<T> handle = stack.pop(); if (handle == null) { handle = stack.newHandle(); handle.value = newObject(handle); } return (T) handle.value; }
這塊邏輯我們并不陌上, stack對象通過pop彈出一個handle
我們跟到pop方法中
DefaultHandle<T> pop() { int size = this.size; if (size == 0) { if (!scavenge()) { return null; } size = this.size; } size --; DefaultHandle ret = elements[size]; elements[size] = null; if (ret.lastRecycledId != ret.recycleId) { throw new IllegalStateException("recycled multiple times"); } ret.recycleId = 0; ret.lastRecycledId = 0; this.size = size; return ret; }
這里我們重點關注, 如果size為空, 也就是當前tack為空的情況下, 會走到scavenge方法, 這個方法, 就是從WeakOrderQueue獲取對象的方法
跟進scavenge方法
boolean scavenge() { if (scavengeSome()) { return true; } prev = null; cursor = head; return false; }
scavengeSome方法表示已經(jīng)回收到了對象, 則直接返回, 如果沒有回收到對象, 則將prev和cursor兩個指針進行重置
繼續(xù)跟到scavengeSome方法中
boolean scavengeSome() { WeakOrderQueue cursor = this.cursor; if (cursor == null) { cursor = head; if (cursor == null) { return false; } } boolean success = false; WeakOrderQueue prev = this.prev; do { if (cursor.transfer(this)) { success = true; break; } WeakOrderQueue next = cursor.next; if (cursor.owner.get() == null) { if (cursor.hasFinalData()) { for (;;) { if (cursor.transfer(this)) { success = true; } else { break; } } } if (prev != null) { prev.next = next; } } else { prev = cursor; } cursor = next; } while (cursor != null && !success); this.prev = prev; this.cursor = cursor; return success; }
首先拿到cursor指針, cursor指針代表要回收的WeakOrderQueue
如果cursor為空, 則讓其指向頭節(jié)點, 如果頭節(jié)點也空, 說明當前stack沒有與其關聯(lián)的WeakOrderQueue, 則返回false
通過一個布爾值success標記回收狀態(tài)
然后拿到pre指針, 也就是cursor的上一個節(jié)點, 之后進入一個do-while循環(huán)
do-while循環(huán)的終止條件是, 如果沒有遍歷到最后一個節(jié)點并且回收的狀態(tài)為false, 這里我們可以分析到再循環(huán)體里, 是不管遍歷與stack關聯(lián)的WeakOrderQueue, 直到彈出對象為止
跟到do-while循環(huán)中:
首先cursor指針會調(diào)用transfer方法, 該方法表示從當前指針指向的WeakOrderQueue中將元素放入到當前stack中, 如果取出成功則將success設置為true并跳出循環(huán), transfer我們稍后分析, 我們繼續(xù)往下看
如果沒有獲得元素, 則會通過next屬性拿到下一個WeakOrderQueue, 然后會進入一個判斷 if (cursor.owner.get() == null)
owner屬性我們上一小節(jié)提到過, 就是與當前WeakOrderQueue關聯(lián)的一個線程, get方法就是獲得關聯(lián)的線程對象, 如果這個對象為null說明該線程不存在, 則進入if塊, 也就是一些清理的工作
if塊中又進入一個判斷 if (cursor.hasFinalData()) , 這里表示當前的WeakOrderQueue中是否還有數(shù)據(jù), 如果有數(shù)據(jù)則通過for循環(huán)將數(shù)據(jù)通過transfer方法傳輸?shù)疆斍皊tack中, 傳輸成功的, 將success標記為true
transfer方法是將WeakOrderQueue中一個link中的handle往stack進行傳輸, 有關link的相關內(nèi)容, 我們上一小節(jié)也進行過分析
所以這里通過for循環(huán)將每個link的中的數(shù)據(jù)傳輸?shù)絪tack中
繼續(xù)往下看, 如果pre節(jié)點不為空, 則通過 prev.next = next 將cursor節(jié)點進行釋放, 也就是pre的下一個節(jié)點指向cursor的下一個節(jié)點
繼續(xù)往下看else塊中的 prev = cursor
這里表示如果當前線程還在, 則將prev賦值為cursor, 代表prev后移一個節(jié)點
最后通過cursor = next將cursor后移一位, 然后再繼續(xù)進行循環(huán)
循環(huán)結束之后, 將stack的prev和cursor屬性進行保存
我們跟到transfer方法中, 分析如何將WeakOrderQueue中的handle傳輸?shù)絪tack中:
boolean transfer(Stack<?> dst) { Link head = this.head; if (head == null) { return false; } if (head.readIndex == LINK_CAPACITY) { if (head.next == null) { return false; } this.head = head = head.next; } final int srcStart = head.readIndex; int srcEnd = head.get(); final int srcSize = srcEnd - srcStart; if (srcSize == 0) { return false; } final int dstSize = dst.size; final int expectedCapacity = dstSize + srcSize; if (expectedCapacity > dst.elements.length) { final int actualCapacity = dst.increaseCapacity(expectedCapacity); srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd); } if (srcStart != srcEnd) { final DefaultHandle[] srcElems = head.elements; final DefaultHandle[] dstElems = dst.elements; int newDstSize = dstSize; for (int i = srcStart; i < srcEnd; i++) { DefaultHandle element = srcElems[i]; if (element.recycleId == 0) { element.recycleId = element.lastRecycledId; } else if (element.recycleId != element.lastRecycledId) { throw new IllegalStateException("recycled already"); } srcElems[i] = null; if (dst.dropHandle(element)) { continue; } element.stack = dst; dstElems[newDstSize ++] = element; } if (srcEnd == LINK_CAPACITY && head.next != null) { reclaimSpace(LINK_CAPACITY); this.head = head.next; } head.readIndex = srcEnd; if (dst.size == newDstSize) { return false; } dst.size = newDstSize; return true; } else { return false; } }
8-7-2
我們上一小節(jié)分析過, WeakOrderQueue是由多個link組成, 每個link通過鏈表的方式進行關聯(lián), 其中head屬性指向第一個link, tail屬性指向最后一個link
在每個link中有多個handle
在link中維護了一個讀指針readIndex, 標記著讀取link中handle的位置
我們繼續(xù)分析transfer方法
首先獲取頭結點, 并判斷頭結點是否為空, 如果頭結點為空, 說明當前WeakOrderQueue并沒有l(wèi)ink, 返回false
if (head.readIndex == LINK_CAPACITY) 這里判斷讀指針是否為16, 因為link中元素最大數(shù)量就是16, 如果讀指針為16, 說明當前l(fā)ink中的數(shù)據(jù)都被取走了
接著判斷 head.next == null , 表示是否還有下一個link, 如果沒有下一個link, 則說明當前WeakOrderQueue沒有元素了, 則返回false
8-7-3
繼續(xù)往下看, 拿到head節(jié)點的讀指針和head中元素的數(shù)量, 接著計算可以傳輸元素的大小, 如果大小為0, 則返回false
8-7-4
接著, 拿到當前stack的大小, 當前stack大小加上可以傳輸?shù)拇笮”硎緎tack中所需要的容量
if (expectedCapacity > dst.elements.length) 表示如果需要的容量大于當前stack中所維護的數(shù)組的大小, 則將stack中維護的數(shù)組進行擴容, 進入if塊中
擴容之后會返回actualCapacity, 表示擴容之后的大小
再看 srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd) 這步
srcEnd表示可以從Link中取的最后一個元素的下標
srcStart + actualCapacity - dstSize 這里我們進行一個拆分, actualCapacity - dstSize表示擴容后大大小-原stack的大小, 也就是最多能往stack中傳輸多少元素
讀指針+可以往stack傳輸?shù)臄?shù)量, 可以表示往stack中傳輸?shù)淖詈笠粋€下標, 這里的下標和srcEnd中取一個較小的值, 也就是既不能超過stack的容量, 也不能造成當前l(fā)ink中下標越界
繼續(xù)往下看
int newDstSize = dstSize 表示初始化stack的下標, 表示stack中從這個下標開始添加數(shù)據(jù)
然后判斷 srcStart != srcEnd , 表示能不能同link中獲取內(nèi)容, 如果不能, 則返回false, 如果可以, 則進入if塊中
接著拿到當前l(fā)ink的數(shù)組elements和stack中的數(shù)組elements
然后通過for循環(huán), 通過數(shù)組下標的方式不斷的將當前l(fā)ink中的數(shù)據(jù)放入到stack中
for循環(huán)中首先拿到link的第i個元素
接著我們我們關注一個細節(jié)
if (element.recycleId == 0) { element.recycleId = element.lastRecycledId; } else if (element.recycleId != element.lastRecycledId) { throw new IllegalStateException("recycled already"); }
這里 element.recycleId == 0 表示對象沒有被回收過, 如果沒有被回收過, 則賦值為lastRecycledId, 我們前面分析過lastRecycledId是WeakOrderQueue中的唯一下標, 通過賦值標記element被回收過
然后繼續(xù)判斷 element.recycleId != element.lastRecycledId , 這表示該對象被回收過, 但是回收的recycleId卻不是最后一次回收lastRecycledId, 這是一種異常情況, 表示一個對象在不同的地方被回收過兩次, 這種情況則拋出異常
接著將link的第i個元素設置為null
繼續(xù)往下看:
if (dst.dropHandle(element)) { continue; }
這里表示控制回收站回收的頻率, 之前的小節(jié)我們分析過, 這里不再贅述
element.stack = dst 表示將handle的stack屬性設置到當前stack
dstElems[newDstSize ++] = element 這里通過數(shù)組的下標的方式將link中的handle賦值到stack的數(shù)組中
繼續(xù)往下看:
if (srcEnd == LINK_CAPACITY && head.next != null) { reclaimSpace(LINK_CAPACITY); this.head = head.next; }
這里的if表循環(huán)結束后, 如果link中的數(shù)據(jù)已經(jīng)回收完畢, 并且還有下一個節(jié)點則會進到reclaimSpace方法
我們跟到reclaimSpace方法
private void reclaimSpace(int space) { assert space >= 0; availableSharedCapacity.addAndGet(space); }
這里將availableSharedCapacity加上16, 表示W(wǎng)eakOrderQueue還可以繼續(xù)插入link
繼續(xù)看transfer方法
this.head = head.next 表示將head節(jié)點后移一個元素
head.readIndex = srcEnd 表示將讀指針指向srcEnd, 下一次讀取可以從srcEnd開始
if (dst.size == newDstSize) 表示沒有向stack傳輸任何對象, 則返回false
否則就通過 dst.size = newDstSize 更新stack的大小為newDstSize, 并返回true
以上就是從link中往stack中傳輸數(shù)據(jù)的過程
章節(jié)小結
這一章主要講解了兩個性能優(yōu)化工具了FastThreadLocal和Recycler
FastThreadLocal和jdk的ThreadLocal功能類似, 只是性能更快, 通過FastTreadLocalThread中的threadLocalMap對象, 通過數(shù)組下標的方式進行保存和獲取對象
Recycler是一個輕量級的對象回收站, 用于對象重用, 避免了對象的頻繁創(chuàng)建和減輕gc的壓力
Recycler同線程回收對象是通過一個線程共享的stack實現(xiàn)的, 將對象包裝成handle并存入stack中
Reclyer異線程回收對象是將handle存入一個與stack關聯(lián)的WeakOrderQueue中, 同一個stack中關聯(lián)的不同WeakOrderQueue由不同的線程創(chuàng)建
從Recycler獲取對象時stack中有值, 則可以直接從stack中獲取
如果stack中沒有值則通過stack關聯(lián)的WeakOrderQueue中獲取
以上就是Netty分布式獲取異線程釋放對象源碼剖析的詳細內(nèi)容,更多關于Netty分布式獲取異線程釋放對象的資料請關注腳本之家其它相關文章!
相關文章
springboot實現(xiàn)郵箱發(fā)送(激活碼)功能的示例代碼
這篇文章主要為大家詳細介紹了如何利用springboot實現(xiàn)郵箱發(fā)送(激活碼)功能,文中的示例代碼簡潔易懂,有需要的小伙伴可以跟隨小編一起學習一下2023-10-10注冊中心配置了spring?security后客戶端啟動報錯
這篇文章主要為大家介紹了注冊中心配置了spring?security后客戶端啟動報錯問題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07Java利用反射動態(tài)設置對象字段值的實現(xiàn)
橋梁信息維護需要做到字段級別的權限控制,本文主要介紹了Java利用反射動態(tài)設置對象字段值的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下2024-01-01eclipse中maven的pom.xml文件中增加依賴的方法
日 在Maven項目中,可以使用pom.xml文件來添加依賴包,本文主要介紹了eclipse中maven的pom.xml文件中增加依賴的方法,具有一定的參考價值,感興趣的可以了解一下2023-12-12Spring中的@ConfigurationProperties詳解
這篇文章主要介紹了Spring中的@ConfigurationProperties詳解,ConfigurationProperties注解主要用于將外部配置文件配置的屬性填充到這個Spring Bean實例中,需要的朋友可以參考下2023-09-09MyBatis-Plus中MetaObjectHandler沒生效完美解決
在進行測試時發(fā)現(xiàn)配置的MyMetaObjectHandler并沒有生效,本文主要介紹了MyBatis-Plus中MetaObjectHandler沒生效完美解決,具有一定的參考價值,感興趣的可以了解一下2023-11-11