1. 程式人生 > >Netty原始碼分析第8章(高效能工具類FastThreadLocal和Recycler)---->第5節: 同線程回收物件

Netty原始碼分析第8章(高效能工具類FastThreadLocal和Recycler)---->第5節: 同線程回收物件

 

Netty原始碼分析第八章: 高效能工具類FastThreadLocal和Recycler

 

第五節: 同線程回收物件

 

上一小節剖析了從recycler中獲取一個物件, 這一小節分析在建立和回收是同線程的前提下, recycler是如何進行回收的

回顧第三小節的demo中的main方法:

public static void main(String[] args){
    User user1 = RECYCLER.get();
    user1.recycle();
    User user2 = RECYCLER.get();
    user2.recycle();
    System.out.println(user1
==user2); }

這裡就是一個同線程回收物件的典型場景, 在一個執行緒中將物件建立並且回收, 我們的User物件定義了recycle方法

static class User{
    private final Recycler.Handle<User> handle;
    public User(Recycler.Handle<User> handle){
        this.handle=handle;
    }
    public void recycle(){
        handle.recycle(this);
    }
}

這裡的recycle是通過handle物件的recycle方法實現物件回收的, 這裡實際呼叫的是DefaultHandle的recycle方法

我們跟進recycle方法:

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

這裡如果回收的物件為null, 則丟擲異常

如果不為null, 則通過自身繫結stack的push方法將自身push到stack中

跟到push方法中:

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

這裡首先判斷當前執行緒, 和建立stack的時候儲存的執行緒是否是同一執行緒, 如果是, 說明是同線程回收物件, 則執行pushNow方法將物件放入stack中

跟到pushNow方法中:

private void pushNow(DefaultHandle<?> item) {
    if ((item.recycleId | item.lastRecycledId) != 0) {
        throw new IllegalStateException("recycled already");
    }
    item.recycleId = item.lastRecycledId = OWN_THREAD_ID;
    int size = this.size;
    if (size >= maxCapacity || dropHandle(item)) {
        return;
    }
    if (size == elements.length) {
        elements = Arrays.copyOf(elements, min(size << 1, maxCapacity));
    }
    elements[size] = item;
    this.size = size + 1;
}

如果第一次回收, item.recycleId和item.lastRecycledId都為0, 所以不會進入if塊, 我們繼續往下看

 item.recycleId = item.lastRecycledId = OWN_THREAD_ID 這一步將handle的recycleId和lastRecycledId賦值為OWN_THREAD_ID, OWN_THREAD_ID在每一個recycle中是唯一固定的, 這裡我們只需要記得這個概念就行

然後獲取當前size

如果size超過上限大小, 則直接返回

這裡還有個判斷dropHandle, 我們跟進去:

boolean dropHandle(DefaultHandle<?> handle) {
    if (!handle.hasBeenRecycled) {
        if ((++handleRecycleCount & ratioMask) != 0) {
            return true;
        }
        handle.hasBeenRecycled = true;
    }
    return false;
}

 if (!handle.hasBeenRecycled) 表示當前物件之前是否沒有被回收過, 如果是第一次回收, 這裡會返回true, 然後進入放到if

再看if中的判斷 if ((++handleRecycleCount & ratioMask) != 0) 

handleRecycleCount表示當前位置stack回收了多少次物件(回收了多少次, 不代表回收了多少個物件, 因為不是每次回收都會被成功的儲存在stack), ratioMask我們之前分析過是7, 這裡 (++handleRecycleCount & ratioMask) != 0 表示回收的物件數如果不是8的倍數, 則返回true, 表示只回收1/8的物件

然後將hasBeenRecycled設定為true, 表示已經被回收

回到pushNow方法中:

如果size的大小等於stack中的陣列elements的大小, 則將陣列elements進行擴容

最後將size通過陣列下標的方式將當前handle設定到elements的元素中, 並將size進行自增

以上就是同線程回收物件的邏輯