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的位置往裏寫
以上就是異線程回收對象的邏輯