Netty源碼分析第8章(高性能工具類FastThreadLocal和Recycler)---->第6節: 異線程回收對象

 

Netty源碼分析第八章: 高性能工具類FastThreadLocal和Recyclerhtml

 

第六節: 異線程回收對象數組

 

異線程回收對象, 就是建立對象和回收對象不在同一條線程的狀況下, 對象回收的邏輯緩存

咱們以前小節簡單介紹過, 異線程回收對象, 是不會放在當前線程的stack中的, 而是放在一個WeakOrderQueue的數據結構中, 回顧咱們以前的一個圖:數據結構

8-6-1ide

相關的邏輯, 咱們跟到源碼中:工具

首先從回收對象的入口方法開始, DefualtHandle的recycle方法:源碼分析

public void recycle(Object object) { if (object != value) { throw new IllegalArgumentException("object does not belong to handle"); } stack.push(this); }

這部分咱們並不陌生, 跟到push方法中:性能

void push(DefaultHandle<?> item) { Thread currentThread = Thread.currentThread(); if (thread == currentThread) { pushNow(item); } else { pushLater(item, currentThread); } }

上一小節分析過, 同線程會走到pushNow, 有關具體邏輯也進行了分析this

若是不是同線程, 則會走到pushLater方法, 傳入handle對象和當前線程對象spa

跟到pushLater方法中:

private void pushLater(DefaultHandle<?> item, Thread thread) { Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get(); WeakOrderQueue queue = delayedRecycled.get(this); if (queue == null) { if (delayedRecycled.size() >= maxDelayedQueues) { delayedRecycled.put(this, WeakOrderQueue.DUMMY); return; } if ((queue = WeakOrderQueue.allocate(this, thread)) == null) { return; } delayedRecycled.put(this, queue); } else if (queue == WeakOrderQueue.DUMMY) { return; } queue.add(item); }

首先經過DELAYED_RECYCLED.get()獲取一個delayedRecycled對象

咱們跟到DELAYED_RECYCLED中:

private static final FastThreadLocal<Map<Stack<?>, WeakOrderQueue>> DELAYED_RECYCLED =
        new FastThreadLocal<Map<Stack<?>, WeakOrderQueue>>() { @Override protected Map<Stack<?>, WeakOrderQueue> initialValue() { return new WeakHashMap<Stack<?>, WeakOrderQueue>(); } };

這裏咱們看到DELAYED_RECYCLED是一個FastThreadLocal對象, initialValue方法建立一個WeakHashMap對象, WeakHashMap是一個map, key爲stack, value爲咱們剛纔提到過的WeakOrderQueue

從中咱們能夠分析到, 每一個線程都維護了一個WeakHashMap對象

WeakHashMap中的元素, 是一個stack和WeakOrderQueue的映射, 說明了不一樣的stack, 對應不一樣的WeakOrderQueue

這裏的映射關係能夠舉個例子說明:

好比線程1建立了一個對象, 在線程3進行了回收, 線程2建立了一個對象, 一樣也在線程3進行了回收, 那麼線程3對應的WeakHashMap中就會有兩個元素:

線程1的stack和線程2的WeakOrderQueue, 線程2和stack和線程2的WeakOrderQueue

 

咱們回到pushLater方法中:

繼續往下看:

WeakOrderQueue queue = delayedRecycled.get(this)

拿到了當前線程的WeakHashMap對象delayedRecycled以後, 而後經過delayedRecycled建立對象的線程的stack, 拿到WeakOrderQueue

這裏的this, 就是建立對象的那個線程所屬的stack, 這個stack是綁定在handle中的, 建立handle對象時候進行的綁定

假設當前線程是線程2, 建立handle的線程是線程1, 這裏經過handle的stack拿到線程1的WeakOrderQueue

 if (queue == null) 說明線程2沒有回收過線程1的對象, 則進入if塊的邏輯:

首先看判斷 if (delayedRecycled.size() >= maxDelayedQueues) 

 delayedRecycled.size() 表示當前線程回收其餘建立對象的線程的線程個數, 也就是有幾個其餘的線程在當前線程回收對象

maxDelayedQueues表示最多能回收的線程個數, 這裏若是朝超過這個值, 就表示當前線程不能在回收其餘線程的對象了

經過 delayedRecycled.put(this, WeakOrderQueue.DUMMY) 標記, 建立對象的線程的stack, 所對應的WeakOrderQueue不可用, DUMMY咱們能夠理解爲不可用

 

若是沒有超過maxDelayedQueues, 則經過if判斷中的 WeakOrderQueue.allocate(this, thread) 這種方式建立一個WeakOrderQueue

allocate傳入this, 也就是建立對象的線程對應的stack, 假設是線程1, thread就是當前線程, 假設是線程2

跟到allocate方法中:

static WeakOrderQueue allocate(Stack<?> stack, Thread thread) { return reserveSpace(stack.availableSharedCapacity, LINK_CAPACITY) ? new WeakOrderQueue(stack, thread) : null; }

reserveSpace(stack.availableSharedCapacity, LINK_CAPACITY)表示線程1的stack還能不能分配LINK_CAPACITY個元素, 若是能夠, 則直接經過new的方式建立一個WeakOrderQueue對象

再跟到reserveSpace方法中:

private static boolean reserveSpace(AtomicInteger availableSharedCapacity, int space) { assert space >= 0; for (;;) { int available = availableSharedCapacity.get(); if (available < space) { return false; } if (availableSharedCapacity.compareAndSet(available, available - space)) { return true; } } }

參數availableSharedCapacity表示線程1的stack容許外部線程給其緩存多少個對象, 以前咱們分析過是16384, space默認是16

方法中經過一個cas操做, 將16384減去16, 表示stack能夠給其餘線程緩存的對象數爲16384-16

而這16個元素, 將由線程2緩存

 

回到pushLater方法中:

建立以後經過 delayedRecycled.put(this, queue) 將stack和WeakOrderQueue進行關聯

最後經過queue.add(item), 將建立的WeakOrderQueue添加一個handle

 

講解WeakOrderQueue以前, 咱們首先了解下WeakOrderQueue的數據結構

WeakOrderQueue維護了多個link, link之間是經過鏈表進行鏈接, 每一個link能夠盛放16個handle,

咱們剛纔分析過, 在reserveSpace方法中將 stack.availableSharedCapacity-16 , 其實就表示了先分配16個空間放在link裏, 下次回收的時候, 若是這16空間沒有填滿, 則能夠繼續往裏盛放

若是16個空間都已填滿, 則經過繼續添加link的方式繼續分配16個空間用於盛放handle

WeakOrderQueue和WeakOrderQueue之間也是經過鏈表進行關聯

能夠根據下圖理解上述邏輯:

8-6-2

根據以上思路, 咱們跟到WeakOrderQueue的構造方法中:

private WeakOrderQueue(Stack<?> stack, Thread thread) { head = tail = new Link(); owner = new WeakReference<Thread>(thread); synchronized (stack) { next = stack.head; stack.head = this; } availableSharedCapacity = stack.availableSharedCapacity; }

這裏有個head和tail, 都指向一個link對象, 這裏咱們能夠分析到, 其實在WeakOrderQueue中維護了一個鏈表, head分別表明頭結點和尾節點, 初始狀態下, 頭結點和尾節點都指向同一個節點

簡單看下link的類的定義:

private static final class Link extends AtomicInteger { private final DefaultHandle<?>[] elements = new DefaultHandle[LINK_CAPACITY]; private int readIndex; private Link next; }

每次建立一個Link, 都會建立一個DefaultHandle類型的數組用於盛放DefaultHandle對象, 默認大小是16個

readIndex是一個讀指針, 咱們以後小節會進行分析

next節點則指向下一個link

回到WeakOrderQueue的構造方法中:

owner是對向前線程進行一個包裝, 表明了當前線程

接下來在一個同步塊中, 將當前建立的WeakOrderQueue插入到stack指向的第一個WeakOrderQueue, 也就是stack的head屬性, 指向咱們建立的WeakOrderQueue, 如圖所示

8-6-3

若是線程2建立一個和stack關聯的WeakOrderQueue, stack的head節點就就會指向線程2建立WeakOrderQueue

若是以後線程3也建立了一個和stack關聯的WeakOrderQueue, stack的head節點就會指向新建立的線程3的WeakOrderQueue

而後線程3的WeakOrderQueue再指向線程2的WeakOrderQueue

也就是不管哪一個線程建立一個和同一個stack關聯的WeakOrderQueue的時候, 都插入到stack指向的WeakOrderQueue列表的頭部

這樣就能夠將stack和其餘線程釋放對象的容器WeakOrderQueue進行綁定

 

回到pushLater方法中:

private void pushLater(DefaultHandle<?> item, Thread thread) { Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get(); WeakOrderQueue queue = delayedRecycled.get(this); if (queue == null) { if (delayedRecycled.size() >= maxDelayedQueues) { delayedRecycled.put(this, WeakOrderQueue.DUMMY); return; } if ((queue = WeakOrderQueue.allocate(this, thread)) == null) { return; } delayedRecycled.put(this, queue); } else if (queue == WeakOrderQueue.DUMMY) { return; } queue.add(item); }

根據以前分析的WeakOrderQueue的數據結構, 咱們分析最後一步, 也就是WeakOrderQueue的add方法

咱們跟進WeakOrderQueue的add方法:

void add(DefaultHandle<?> handle) { handle.lastRecycledId = id; Link tail = this.tail; int writeIndex; if ((writeIndex = tail.get()) == LINK_CAPACITY) { if (!reserveSpace(availableSharedCapacity, LINK_CAPACITY)) { return; } this.tail = tail = tail.next = new Link(); writeIndex = tail.get(); } tail.elements[writeIndex] = handle; handle.stack = null; tail.lazySet(writeIndex + 1); }

首先, 看 handle.lastRecycledId = id 

lastRecycledId表示handle上次回收的id, 而id表示WeakOrderQueue的id, weakOrderQueue每次建立的時候, 會爲自增一個惟一的id

 Link tail = this.tail 表示拿到當前WeakOrderQueue的中指向最後一個link的指針, 也就是尾指針

再看 if ((writeIndex = tail.get()) == LINK_CAPACITY) 

tail.get()表示獲取當前link中已經填充元素的個數, 若是等於16, 說明元素已經填充滿

而後經過eserveSpace方法判斷當前WeakOrderQueue是否還能緩存stack的對象, eserveSpace方法咱們剛纔已經分析過, 會根據stack的屬性availableSharedCapacity-16的方式判斷還可否緩存stack的對象, 若是不能再緩存stack的對象, 則返回

若是還能繼續緩存, 則在建立一個link, 並將尾節點指向新建立的link, 而且原來尾節點的next的節點指向新建立的link

而後拿到當前link的writeIndex, 也就是寫指針, 若是是新建立的link中沒有元素, writeIndex爲0

以後將尾部的link的elements屬性, 也就是一個DefaultHandle類型的數組, 經過數組下標的方式將第writeIndex個節點賦值爲要回收的handle

而後將handle的stack屬性設置爲null, 表示當前handle不是經過stack進行回收的

最後將tail節點的元素個數進行+1, 表示下一次將從writeIndex+1的位置往裏寫

以上就是異線程回收對象的邏輯

 

上一節: 同線程回收對象

下一節: 獲取異線程釋放的對象

相關文章
相關標籤/搜索