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