Netty源碼分析-內存模型PoolSubpage

 

 

PoolSubpage源碼分析-這個方法獲取一個long表示的64個比特位置是否還有空餘空間

  private int findNextAvail() {  
        // 沒有明確的可用位置時則挨個查找  
        final long[] bitmap = this.bitmap;  
        final int bitmapLength = this.bitmapLength;  
        for (int i = 0; i < bitmapLength; i ++) {  
            long bits = bitmap[i];  
            //~bits 把字節0變1 1變0,如果一個long類型64個比特全部爲1,那麼反過來得0,說明沒有位置了。
            // 說明這個位置段中還有可以分配的element  
           if (~bits != 0) {  
                return findNextAvail0(i, bits);  
            }  
        }  
        return -1;  
    } 

 

PoolSubpage源碼分析-這個方法查詢出一個long類型64個比特當中可用的比特位,在加上一個基數,就計算出了對應的內存快的位置。比如當前內存按16字節分塊,那麼8192/16=512,一共內存分成512塊, 在bits當中找到第一個比特爲0的位置,比如70,假設基礎爲64,那麼64+70=134就是對應空閒的內存快。

    private int findNextAvail0(int i, long bits) {  
        final int maxNumElems = this.maxNumElems;  
        //重點:首先右移位6爲,相當於乘以8, 因爲i的範圍從0-7
        //i=0的long的64個比特表示0-63個位置,  i=1的long表示64-127的位置。
        //所以baseVal相當於這個基數,如果i=0,那麼baseVal=0,如果i=1,baseVal=64, i=2,baseVal=128
        final int baseVal = i << 6;  
         
        //循環64次,依次拿出long的每個比特進行判斷
        for (int j = 0; j < 64; j ++) {  
             
            //&倆邊爲1結果爲1否則爲0
            //bits & 1 相當於判斷bits的最後一個比特位,
            // 如果該位置的值爲0,表示還未分配  
            if ((bits & 1) == 0) {  
                // |  倆邊爲0結果爲0 否則爲1
                //j從0-63,baseVal | j 相當於 baseVal+j, j就是long裏面可用比特的位置,但是需要加上基數。
                int val = baseVal | j;  
                
                //這裏要判斷一下,因爲如果當前的ElementSize不是16,假設爲32,那麼maxNumElems=256   
                //bitmap長度爲8,64x8=512,表示512個位置,但其實最大位置也就256,所以需要判斷一下,只有ElementSize=16時這個判斷纔沒有意義,因爲16會被分成512份,正好。
                if (val < maxNumElems) {  
                    return val;  
                } else {  
                    break;  
                }  
            }  
            //否則無符號右移1爲,相當於把原來bits的第二個比特移到第一個位置。
            bits >>>= 1;  
        }  
        return -1;  
    }

 

PoolSubpage源碼分析-分配一個可用的內存分片。

    long allocate() {
        //這裏不知道什麼意思
        if (elemSize == 0) {
            return toHandle(0);
        }
        
        //如果可用的內存分片爲0,或者doNotDestroy=false
        //說明無內存可用,返回-1
        if (numAvail == 0 || !doNotDestroy) {
            return -1;
        }
        
        //找到一個可用的內存分片的index,假設65
        final int bitmapIdx = getNextAvail();
        //右移6爲等於除以64, q=1,說明要放在bitmap[1]這個位置
        //用bitmap[1]這個long的比特表示120這個位置的狀態。
        int q = bitmapIdx >>> 6;
        //把高位全部去掉,只留下一個0-63的數(正好64種可能)
        //r表示0-63當中的某一個位置   r=1,
        //0011010101...(56比特)+(8比特)000000r1,  r=65所在的比特位置
        //0011010101...(56比特)+(8比特)0000000r,  r=64所在的比特位置
        int r = bitmapIdx & 63;
        //把r移到最右面,必須等於0,所以爲被分配
        assert (bitmap[q] >>> r & 1) == 0;
        //1L左移R位,相當於複製  在|拼接原始值
        bitmap[q] |= 1L << r;
        
        //如果可用內存分片爲0,則從隊列中移除
        if (-- numAvail == 0) {
            removeFromPool();
        }
        
        //返回一個long類型的handle,裏面包含了內存的index,還有當前subpage所屬的16M內存當中的index
        return toHandle(bitmapIdx);
    }

 

PoolSubpage源碼分析,這個方法把3個數字按比特排列方式拼接爲1個數字,等使用時在把這三個數字還原。

    private long toHandle(int bitmapIdx) {
        //把3個數字拼接爲一個,一個long類型64個比特,拼接在一起個佔個的位置,等需要時在進行拆分得到原先的3個數字。
        return 0x4000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx;
    }

 

PoolSubpage源碼分析添加到隊列和從隊列中移除,當前對象會加入到arena當中管理。

    //把隊列插入到頭的後面, 比如原來的順序爲  head  - x - y
    //當前對象爲this,那麼拼接後的順序爲  head - this -x -y
    private void addToPool(PoolSubpage<T> head) {
        assert prev == null && next == null;
        //當前對象的前一個指head
        prev = head;
        //當前對象的後一個指head的下一個
        next = head.next;
        next.prev = this;
        head.next = this;
    }
    
    //把當前對象從鏈表中移除
    //head - x - this - y  移除後  head - x -y
    private void removeFromPool() {
        assert prev != null && next != null;
        prev.next = next;
        next.prev = prev;
        next = null;
        prev = null;
    }

 

   //構造方法   
 PoolSubpage(PoolSubpage<T> head, PoolChunk<T> chunk, int memoryMapIdx, int runOffset, int pageSize, int elemSize) {
        //所屬chunk
        this.chunk = chunk;
        //所屬chunk整個內存分塊的下標值,默認chunk爲16M,每頁大小8192,分爲2048個塊,那麼memoryMapIdx表代表了哪一塊
        this.memoryMapIdx = memoryMapIdx;
        //整體內存的偏移量,比如整體內存爲16M的byte素組,那runOffset就是數組下標的起點
        this.runOffset = runOffset;
        //每頁大小,默認8192
        this.pageSize = pageSize;
        //long類型素組, pageSize / 16 / 64
        //因爲最小單元把當前8192字節拆分爲16個字節一個單元,8192/16=512 個單元
         //一個long的64個比特位表示64個單元,那麼需要512/64=8個long類型的數字表示
        bitmap = new long[pageSize >>> 10]; // pageSize / 16 / 64
        init(head, elemSize);
    }

 

 

   void init(PoolSubpage<T> head, int elemSize) {
        //設置不可回收
        doNotDestroy = true;
        //單元大小,16 或 32 或 64 。。。。。
        this.elemSize = elemSize;

        //假設16比特位單元大小
        if (elemSize != 0) {
            //8192/16=512
            //那麼maxNumElems 和 numAvail = 512
            maxNumElems = numAvail = pageSize / elemSize;
            
            //可用位置從0開始
            nextAvail = 0;
            
            //512 除以64 = 8,表示bitmap數組需要使用幾個元素,當單元大小爲16時,需要8個元素
            //這也是最多的一種情況,如果單元大小爲32時,maxNumElems=256,那麼256/64=4,那麼需要4個元素就能滿足情況了
            bitmapLength = maxNumElems >>> 6;
            if ((maxNumElems & 63) != 0) {
                bitmapLength ++;
            }
            
            //把元素都設置爲0
            for (int i = 0; i < bitmapLength; i ++) {
                bitmap[i] = 0;
            }
        }
        
        //加入到arena的隊列中管理
        addToPool(head);
    }

 

 

  boolean free(PoolSubpage<T> head, int bitmapIdx) {
        if (elemSize == 0) {
            return true;
        }
        //假設bitmapIdx=65
        //除以64計算出數組下標  q=1
        int q = bitmapIdx >>> 6;  
        //計算出所在0-63當中的位置,r=1
        int r = bitmapIdx & 63;
        //把值移到最右面 必須不能爲0
        assert (bitmap[q] >>> r & 1) != 0;
        
        //如果相對應位值相同,則結果爲0,否則爲1
        //左移1爲,得到 xxxx.....1x(64bit)
        //把xxxx.....1x 變爲xxxx.....0x,其實就是把r位置的比特設置爲0
        bitmap[q] ^= 1L << r;
        
        //設置下一個可用位置爲當前的bitmapIdx
        setNextAvail(bitmapIdx);
        
        //先判斷在++,如果numAvail ==0 說明沒在池中,需要加入池,然後++
        if (numAvail ++ == 0) {
            addToPool(head);
            return true;
        }
        
        //如果可用單元沒達到最大值,說明還有空餘空間
        if (numAvail != maxNumElems) {
            return true;
        } else {
            //如果Subpage空閒
            // Subpage not in use (numAvail == maxNumElems)
            
            //判斷一下當前對象是不是arena數組中head的下一個元素,如果是則保留當前對象,否則將其從pool中移除。  
            //目的:這樣儘可能的保證arena分配小內存時能直接從pool中取,而不用再到chunk中去獲取。  
            if (prev == next) {
                // Do not remove if this subpage is the only one left in the pool.
                return true;
            }

            // Remove this subpage from the pool if there are other subpages left in the pool.
            //把當前對象從隊列移除,返回false,調用者會根據返回值做處理,會把他回收掉,同時把父類對應的8192空間釋放爲可用。
            doNotDestroy = false;
            removeFromPool();
            return false;
        }
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章