<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Netty對(duì)象池

          共 10683字,需瀏覽 22分鐘

           ·

          2021-06-12 13:47


              在平時(shí)工作中,聽(tīng)說(shuō)和使用過(guò)連接池,線程池等.還有一種就是對(duì)象池,可以實(shí)現(xiàn)對(duì)象復(fù)用的功能.

          當(dāng)然實(shí)現(xiàn)對(duì)象池的方式手段有多種,比如有一個(gè)公共的池子,所有需要對(duì)象的線程通過(guò)并發(fā)控制的方式從池子中獲取對(duì)象,并發(fā)控制的同時(shí)伴隨性能的損耗.那么Netty是如何實(shí)現(xiàn)對(duì)象池的呢? 先通過(guò)一段演示代碼說(shuō)起

          import io.netty.util.Recycler;
          public class Book {
          private String name; private final Recycler.Handle<Book> recyclerHandle;
          Book(Recycler.Handle<Book> recyclerHandle) { this.recyclerHandle = recyclerHandle; }
          public void setName(String name) { this.name = name; }
          void recycle() { recyclerHandle.recycle(this); }}



          import io.netty.util.Recycler;import io.netty.util.concurrent.FastThreadLocalThread;
          import java.util.concurrent.locks.LockSupport;
          public class Main {

          private static final Recycler<Book> BOOK = new Recycler<Book>() { @Override protected Book newObject(Handle<Book> handle) { return new Book(handle); } };

          private static Book book1; private static Book book2; private static Book book3;
          public static void main(String[] args) throws Exception {

          new FastThreadLocalThread(() -> { // book1,book2,book3都是從線程Thread-1中創(chuàng)建產(chǎn)生 book1 = BOOK.get(); book1.setName("Java");
          book2 = BOOK.get(); book2.setName("C");
          book3 = BOOK.get(); book3.setName("C++"); // book1在線程Thread-1中回收 book1.recycle();
          new FastThreadLocalThread(() -> { // book2在線程Thread-2中回收 book2.recycle(); LockSupport.park();// 讓線程不退出,便于觀察 }, "Thread-2").start();

          new FastThreadLocalThread(() -> { // book3在線程Thread-3中回收 book3.recycle(); LockSupport.park();// 讓線程不退出,便于觀察 }, "Thread-3").start();

          LockSupport.park();// 讓線程不退出,便于觀察 }, "Thread-1").start();

          // 讓線程不退出,便于觀察 LockSupport.park();
          }}




          以上代碼,在線程Thread-1中創(chuàng)建了book1(Java),book2(C),book3(C++)這三個(gè)對(duì)象.然后在線程Thread-1中回收book1對(duì)象,在線程Thread-2中回收book2對(duì)象,在線程Thread-3中回收book3對(duì)象.

          通過(guò)jps命令查看進(jìn)程ID



          通過(guò)jmap命令dump堆內(nèi)存


          jmap -dump:format=b,file=2021-heap.hprof 10624



          接下來(lái)使用Eclipse的MAT工具打開(kāi)上面創(chuàng)建生成的2021-heap.hprof文件.



          點(diǎn)擊上圖中的按鈕,查看線程.




          找到線程Thread-1的內(nèi)存地址=0xd786b000

          然后根據(jù)這個(gè)地址,查看線程內(nèi)部屬性信息.





          線程Thread-1內(nèi)部屬性信息如下圖所示




          其中,需要關(guān)注threadLocalMap屬性.



          關(guān)于FastThreadLocal的內(nèi)容,之前的文章有介紹.





          這里也附著一張F(tuán)astThreadLocal的圖


          代碼中創(chuàng)建的book1,book2,book3這三個(gè)對(duì)象,就存放在threadLocalMap屬性里面.依次展開(kāi)它.

          首先找到了book1對(duì)象



          繼續(xù)找到了book2對(duì)象,而且這個(gè)book2對(duì)象和線程Thread-2有關(guān)系.



          最后找到了book3對(duì)象,而且這個(gè)book3對(duì)象和線程Thread-3有關(guān)系.



          根據(jù)以上堆數(shù)據(jù)的分析,可以得出如下簡(jiǎn)單關(guān)系.




          如上圖,線程Thread-1創(chuàng)建了book1,book2,book3這三個(gè)對(duì)象. 回收之后,book1對(duì)象在線程Thread-1里,book2對(duì)象在線程Thread-2里,book3對(duì)象在線程Thread-3里.

          接下來(lái),通過(guò)分析源碼的方式,完善這張圖.

          我們先將上面一開(kāi)始的代碼,簡(jiǎn)化下,如下圖所示



          根據(jù)上圖,得到如下一個(gè)引用關(guān)系


          獲取對(duì)象路徑


          Thread -> Recycler -> 目標(biāo)對(duì)象



          線程如果需要對(duì)象,不能在像以前那樣手動(dòng)創(chuàng)建對(duì)象(比如new Book()),而是需要借助Recycler對(duì)象,通過(guò)它得到所需要的Book對(duì)象.

          因此,Recycler是一個(gè)很重要的類(lèi)

          io.netty.util.Recycler

          在Recycler內(nèi)部有一個(gè)很重要的屬性,如下


          private final FastThreadLocal<Stack<T>> threadLocal = new FastThreadLocal<Stack<T>>() {    @Override    protected Stack<T> initialValue() {        return new Stack<T>(Recycler.this, Thread.currentThread(), maxCapacityPerThread, maxSharedCapacityFactor,                            interval, maxDelayedQueuesPerThread, delayedQueueInterval);    }
          @Override protected void onRemoval(Stack<T> value) { if (value.threadRef.get() == Thread.currentThread()) { if (DELAYED_RECYCLED.isSet()) { DELAYED_RECYCLED.get().remove(value); } } }};


          通過(guò)線程局部對(duì)象FastThreadLocal, 也就是說(shuō), 當(dāng)每個(gè)線程在使用Recycler獲取所需要的對(duì)象的時(shí)候,它的內(nèi)部是實(shí)際上是從每個(gè)線程的Stack中獲取對(duì)象.




          小提示



          每個(gè)線程都會(huì)有一個(gè)Stack. 當(dāng)然這句話也不是這么絕對(duì), 如果一個(gè)線程它使用了n個(gè)Recycler,那么這個(gè)線程就擁有n個(gè)Stack. 為了說(shuō)明這一點(diǎn),驗(yàn)證代碼如下



          如果在一個(gè)線程中,使用多個(gè)Recycler對(duì)象.


          import io.netty.util.Recycler;import io.netty.util.concurrent.FastThreadLocalThread;

          public class Main {
          private static final Recycler<Book> BOOK_CN = new Recycler<Book>() { @Override protected Book newObject(Handle<Book> handle) { return new Book(handle); } };
          private static final Recycler<Book> BOOK_EN = new Recycler<Book>() { @Override protected Book newObject(Handle<Book> handle) { return new Book(handle); } };
          public static void main(String[] args) throws Exception {
          new FastThreadLocalThread(() -> { // 在同一個(gè)線程中使用2個(gè)Recycler實(shí)例,則會(huì)創(chuàng)建2個(gè)Stack Book book1 = BOOK_CN.get(); Book book2 = BOOK_EN.get();
          }, "Thread-1").start();
          }}




          如上圖,線程Thread-1它擁有2個(gè)Stack對(duì)象.

          接下來(lái)繼續(xù)分析Netty的對(duì)象池, 代碼中是通過(guò)io.netty.util.Recycler#get方法獲取對(duì)象的,追蹤此方法.


          public final T get() {    // 如果沒(méi)有啟用線程池,則每次獲取對(duì)象都要新創(chuàng)建對(duì)象.    if (maxCapacityPerThread == 0) {        return newObject((Handle<T>) NOOP_HANDLE);    }    // 每個(gè)線程獲取它自己對(duì)應(yīng)的Stack對(duì)象.    Stack<T> stack = threadLocal.get();    // 從Stack中'彈出'一個(gè)對(duì)象.    DefaultHandle<T> handle = stack.pop();    if (handle == null) {        // 如果Stack中沒(méi)有DefaultHandle對(duì)象,則新創(chuàng)建一個(gè)DefaultHandle        handle = stack.newHandle();        // 創(chuàng)建線程需要的那個(gè)對(duì)象        handle.value = newObject(handle);    }    // 返回線程需要的那個(gè)對(duì)象    return (T) handle.value;}


          根據(jù)以上代碼,得出如下關(guān)系.





          Thread -> Recycler -> Stack -> DefaultHandle -> 目標(biāo)對(duì)象




          一個(gè)線程,要想得到所需要的目標(biāo)對(duì)象,需要經(jīng)過(guò)Recycler->Stack->DefaultHandle之后,才能拿到目標(biāo)對(duì)象.

          之前dump出來(lái)的堆內(nèi)存,也能看到目標(biāo)對(duì)象是'包裹'在DefaultHandle對(duì)象中的.



          接下來(lái)看下它是怎么從Stack中'彈出'一個(gè)對(duì)象的.


          DefaultHandle<T> pop() {    int size = this.size;    if (size == 0) {        // 這個(gè)地方后面會(huì)說(shuō)        if (!scavenge()) {            return null;        }        size = this.size;        if (size <= 0) {            return null;        }    }    size --;    // 從elements數(shù)組中拿出最后一個(gè)元素    DefaultHandle ret = elements[size];    elements[size] = null;
          this.size = size;
          if (ret.lastRecycledId != ret.recycleId) { throw new IllegalStateException("recycled multiple times"); } ret.recycleId = 0; ret.lastRecycledId = 0; // 將元素直接返回 return ret;}


          在Stack內(nèi)部有個(gè)數(shù)組,用來(lái)'裝'DefaultHandle,當(dāng)需要的時(shí)候,直接從這個(gè)數(shù)組中拿出最后一個(gè)元素DefaultHandle返回.

          到目前為止,看一下此時(shí)的結(jié)構(gòu)




          正所謂'有借有還',既然它是一個(gè)對(duì)象池,當(dāng)使用完之后,需要調(diào)用回收方法. 在文章一開(kāi)始我們自己設(shè)計(jì)的Book類(lèi)中也實(shí)現(xiàn)了回收方法.


          public class Book {
          private String name;
          private final Recycler.Handle<Book> recyclerHandle;
          Book(Recycler.Handle<Book> recyclerHandle) { this.recyclerHandle = recyclerHandle; }
          public void setName(String name) { this.name = name; }
          // 回收方法 void recycle() { // 實(shí)際調(diào)用io.netty.util.Recycler.DefaultHandle#recycle方法 recyclerHandle.recycle(this); }
          }


          回收操作就是從這個(gè)io.netty.util.Recycler.DefaultHandle#recycle方法開(kāi)始的.


          @Overridepublic void recycle(Object object) {    if (object != value) {        throw new IllegalArgumentException("object does not belong to handle");    }  // 得到相應(yīng)的Stack    // 每個(gè)DefaultHandle有且僅屬于一個(gè)Stack    Stack<?> stack = this.stack;    if (lastRecycledId != recycleId || stack == null) {        throw new IllegalStateException("recycled already");    }  // 將DefaultHandle對(duì)象放入Stack中    stack.push(this);}



          還要說(shuō)一點(diǎn)是,如下圖,比如線程Thread-1創(chuàng)建一個(gè)book對(duì)象,第一種情況,book對(duì)象使用完之后,最后是由線程Thread-1回收它的(誰(shuí)創(chuàng)建誰(shuí)回收). 第二種情況,book對(duì)象交給了線程Thread-3使用,最后由線程Thread-3回收它(他人創(chuàng)建我來(lái)回收).



          根據(jù)上圖,再理解下push源碼


          void push(DefaultHandle<?> item) {    // 得到當(dāng)前線程    Thread currentThread = Thread.currentThread();        // 如果當(dāng)前線程和Stack對(duì)應(yīng)的線程是同一個(gè)線程    // 每個(gè)Stack有且只屬于一個(gè)線程.    if (threadRef.get() == currentThread) {        pushNow(item);    } else {        // 當(dāng)前線程和Stack對(duì)應(yīng)的線程不是同一個(gè)線程        pushLater(item, currentThread);    }}


          將對(duì)象放入Stack分2種情況,第一種是pushNow,第二種是pushLater.


          private void pushNow(DefaultHandle<?> item) {    if (item.recycleId != 0 || !item.compareAndSetLastRecycledId(0, OWN_THREAD_ID)) {        throw new IllegalStateException("recycled already");    }    item.recycleId = OWN_THREAD_ID;
          int size = this.size; // dropHandle方法用來(lái)控制放入Stack的速率,有點(diǎn)類(lèi)似流控,稍后再說(shuō) if (size >= maxCapacity || dropHandle(item)) { return; } if (size == elements.length) { elements = Arrays.copyOf(elements, min(size << 1, maxCapacity)); } // 直接將元素放入elements數(shù)組的最后 elements[size] = item; this.size = size + 1;}


          pushNow方法很簡(jiǎn)單,直接將DefaultHandle元素放入到數(shù)組即可.

          pushLater方法有點(diǎn)麻煩.首先你要記得,執(zhí)行pushLater的線程一定不是歸屬Stack的線程.

          說(shuō)白了,就是由其他線程'協(xié)助'歸屬Stack的線程來(lái)做push操作.


          // 1.先將Queue鏈接到鏈表上// 2.將item添加到Queue#Linkprivate void pushLater(DefaultHandle<?> item, Thread thread) {
          // DELAYED_RECYCLED是FastThreadLocal類(lèi)型, 則每個(gè)線程都有一個(gè)DELAYED_RECYCLED. // 每個(gè)線程會(huì)維護(hù) Stack -> WeakOrderQueue 映射關(guān)系, 即每個(gè)線程會(huì)'協(xié)助'其他線程存儲(chǔ)它的對(duì)象. 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; } // 創(chuàng)建queue,同時(shí)鏈接到鏈表上 if ((queue = WeakOrderQueue.allocate(this, thread)) == null) { // drop object return; } delayedRecycled.put(this, queue); } else if (queue == WeakOrderQueue.DUMMY) { // drop object return; }
          queue.add(item);}




          結(jié)合上圖,再梳理下pushLater邏輯

          1.首先線程Thread-3先從Map中查找對(duì)應(yīng)Stack的Queue. 第一次肯定找不到,于是新建一個(gè)Queue,然后再把這個(gè)新建的Queue鏈接到Stack上.

          2.將元素添加到Queue.

          到了現(xiàn)在,我們可以放一張全局圖了



          如上圖,Stack對(duì)象是隸屬于線程thread-1的. 如果從Stack中獲取的對(duì)象,最后也是由線程thread-1回收的,那么對(duì)象就會(huì)存到Stack中的elements數(shù)組中.

          如果從Stack中獲取的對(duì)象,最后是由線程thread-2回收,那么thread-2就會(huì)創(chuàng)建一個(gè)針對(duì)此Stack的Queue,鏈接到Queue鏈上.然后再把對(duì)象放到相應(yīng)的Link中的elements數(shù)組中.

          還有一點(diǎn),在回收對(duì)象的時(shí)候,并不是'全部都回收'. 而是默認(rèn)每隔8個(gè)回收一個(gè)對(duì)象. 在pushNow和pushLater方法內(nèi)部都會(huì)調(diào)用如下方法


          boolean dropHandle(DefaultHandle<?> handle) {    // 1.如果當(dāng)前handle之前被回收過(guò),那么此次也會(huì)被回收    // 2.如果當(dāng)前handle之前沒(méi)有回收過(guò),那么默認(rèn)每隔8個(gè)回收一個(gè),防止Stack的DefaultHandle[]數(shù)組發(fā)生爆炸性的增長(zhǎng).    if (!handle.hasBeenRecycled) {        if ((++handleRecycleCount & ratioMask) != 0) {            // Drop the object.            return true;        }        handle.hasBeenRecycled = true; // 標(biāo)記元素被回收    }    return false;}



          在從Stack獲取元素的時(shí)候,代碼如下



          DefaultHandle<T> pop() {    int size = this.size;    if (size == 0) {// 如果elements中沒(méi)有可用元素        if (!scavenge()) {// 從其他WeakOrderQueue中轉(zhuǎn)移數(shù)據(jù)到當(dāng)前stack#elements中            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;}


          默認(rèn)先從Stack自己的elements中獲取元素,如果elements中沒(méi)有元素的時(shí)候,則從Queue鏈上轉(zhuǎn)移數(shù)據(jù)到當(dāng)前Stack的elements數(shù)組中.

          分析到這里,我們可以總結(jié)下Netty對(duì)象池的實(shí)現(xiàn)了.
          每個(gè)線程都有一個(gè)Stack用于'裝載'需要復(fù)用的對(duì)象. 同時(shí)其他線程也會(huì)'協(xié)助'它回收對(duì)象. 如果Stack中沒(méi)有對(duì)象了,那么會(huì)從其他線程的Queue中轉(zhuǎn)移對(duì)象到Stack中. 如果是Stack隸屬的線程回收對(duì)象,那么對(duì)象會(huì)被放到Stack的elements數(shù)組中,如果是其他線程回收對(duì)象,那么會(huì)把這個(gè)對(duì)象放到其他線程維護(hù)的Queue中.



          瀏覽 44
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久久久久久久久高清毛片一级 | 中国老太卖婬视频播放 | 天堂欧美城网站网址 | www999女优館xxxcoh | 安徽妇女BBBWBBBwm |