zoukankan      html  css  js  c++  java
  • [编织消息框架][netty源码分析]14 PoolChunk 的 PoolSubpage

    final class PoolSubpage<T> implements PoolSubpageMetric {
    
        //该page分配的chunk
        final PoolChunk<T> chunk;
        //内存使用记录
        private final long[] bitmap;
        //该page是否已释放
        boolean doNotDestroy;
        
        //该page在chunk中的id,通过区段计算偏移
        private final int memoryMapIdx;
        //该page在chunk.memory的偏移量
        private final int runOffset;
        //page大小 
        private final int pageSize;
        //page切分后每一段的大小 最少等于16通过 PoolArena#normalizeCapacity方法计算
        int elemSize;
        //page包含的段数量
        private int maxNumElems;
        private int bitmapLength;
        //下一个可用的位置,用于优化findNextAvail计算
        private int nextAvail;
        //可用的段数量
        private int numAvail;
        
        //每个分页大小 pageSize = 8192(8k)
        //分页里每个单元大小 elemSize最少等于16 通过 PoolArena#normalizeCapacity方法计算
        void init(PoolSubpage<T> head, int elemSize) {
            doNotDestroy = true;
            this.elemSize = elemSize;
            if (elemSize != 0) {
                maxNumElems = numAvail = pageSize / elemSize;
                nextAvail = 0;
                
                //所有数据除以64分段 >>>6 等于 /(6*6)
                bitmapLength = maxNumElems >>> 6;
                //最后边界加1处理
                if ((maxNumElems & 63) != 0) {
                    bitmapLength ++;
                }
                //初始化bitmap清零
                for (int i = 0; i < bitmapLength; i ++) {
                    bitmap[i] = 0;
                }
            }
        }
        
        long allocate() {
            if (elemSize == 0) {
                return toHandle(0);
            }
            if (numAvail == 0 || !doNotDestroy) {
                return -1;
            }
            final int bitmapIdx = getNextAvail();
            //计算出在那一段
            int q = bitmapIdx >>> 6;
            
            //等于(运算值+1)的倍数,那么结果就为0。如bits & 63 当bits等于64的倍数时结果为0,否则取min(bits,64) 或 bits/64 余数
            //这里意思是取当前段的所有状态
            int r = bitmapIdx & 63;
            //设置相应位置状态
            bitmap[q] |= 1L << r;
    
            //没有可以执行回收
            if (-- numAvail == 0) {
                removeFromPool();
            }
    
            return toHandle(bitmapIdx);
        }
        
        //这里做了优化,当执行free时会重新计算nextAvail,这时可直接返回
        private int getNextAvail() {
            int nextAvail = this.nextAvail;
            if (nextAvail >= 0) {
                this.nextAvail = -1;
                return nextAvail;
            }
            return findNextAvail();
        }
        private int findNextAvail() {
            final long[] bitmap = this.bitmap;
            final int bitmapLength = this.bitmapLength;
            for (int i = 0; i < bitmapLength; i ++) {
                long bits = bitmap[i];
                //取反不等于0说明还有空位可以使用
                if (~bits != 0) {
                    return findNextAvail0(i, bits);
                }
            }
            return -1;
        }
    
        private int findNextAvail0(int i, long bits) {
            final int maxNumElems = this.maxNumElems;
            //算出在那一段基础值
            final int baseVal = i << 6;
            //这里用到 >>> 即每位检查是否占用,如果没有占到计算实际id返回 val = baseVal | j
            for (int j = 0; j < 64; j ++) {
                // bits & 1 转换成 bits & b01 该第一位二进制没有状态说明该位置没使用
                if ((bits & 1) == 0) {
                    //算出当前段第几位
                    int val = baseVal | j;
                    //少于maxNumElems直接返回,否则返回-1
                    if (val < maxNumElems) {
                        return val;
                    } else {
                        break;
                    }
                }
                
                //>>>= 右移(向低位移动),左边空出位(高位)以0填充
                //>>>1降低一位
                bits >>>= 1;
            }
            return -1;
        }
        
        private long toHandle(int bitmapIdx) {
            return 0x4000000000000000L | (long) bitmapIdx << 32 | memoryMapIdx;
        }
        
        //返回true已在使用,返回false已释放
        boolean free(PoolSubpage<T> head, int bitmapIdx) {
            if (elemSize == 0) {
                return true;
            }
            int q = bitmapIdx >>> 6;
            int r = bitmapIdx & 63;
            bitmap[q] ^= 1L << r;
    
            nextAvail = bitmapIdx;
    
            if (numAvail ++ == 0) {
                addToPool(head);
                return true;
            }
    
            if (numAvail != maxNumElems) {
                return true;
            } else {
                // Subpage not in use (numAvail == maxNumElems)
                if (prev == next) {
                    // Do not remove if this subpage is the only one left in the pool.
                    //subpage是pool唯一的,不能删除
                    return true;
                }
                //设置删除记录
                doNotDestroy = false;
                removeFromPool();
                return false;
            }
        }
    }

    通过PoolSubpage构造时打断点追踪如何分配pageSize,elemSize

    PoolArena normalizeCapacity方法分配 elemSize

    static int normalizeCapacity(int reqCapacity) {
            // 大等于512时 双倍增加
            if ((reqCapacity & -512) != 0) {
                // 这里为什么不用 normalizedCapacity<<1 直接加倍,有可能normalizedCapacity刚好是512倍数
    
                int normalizedCapacity = reqCapacity;
                // 减一是避免双倍自增
                normalizedCapacity--;
                // 将低四位的二进制设置为1,一个int是32位
                // 注释掉代码是优化过的,逻辑用for简明示例
                // normalizedCapacity |= normalizedCapacity >>> 1;
                // normalizedCapacity |= normalizedCapacity >>> 2;
                // normalizedCapacity |= normalizedCapacity >>> 4;
                // normalizedCapacity |= normalizedCapacity >>> 8;
                // normalizedCapacity |= normalizedCapacity >>> 16;
                for (int i = 1; i <= 16; i*=2) {
                normalizedCapacity |= reqCapacity >>> i;
                }
                // 最后要加回1
                normalizedCapacity++;
    
                // 少于0去掉最高位1即可变正数
                if (normalizedCapacity < 0) {
                normalizedCapacity >>>= 1;
                }
    
                return normalizedCapacity;
            }
            // 少于512情况
            // 刚好16倍数直接返回
            if ((reqCapacity & 15) == 0) {
                return reqCapacity;
            }
            // &~15 相当于 &-16
            // 如果少于16结果为0,否则大于16取16的倍数
            return (reqCapacity & ~15) + 16;
        }

    我们使用简化的方式来实现PoolSubpage bitmap处理逻辑,方便读者明白如何用long来做储存,应用大量的 & |  >>> 等运算

    public class TestPoolSubpage {
        private static int pageSize = 1024*8;
        private static int maxNumElems = pageSize / 16;
        private static int bitmapLength = maxNumElems >> 6;
        static{
         if ((maxNumElems & 63) != 0) {
                 bitmapLength ++;
             } 
        }
        private static List<Boolean>[] bitmap = new ArrayList[bitmapLength];
    
        public static void main(String[] args) {
            for (int i = 0; i < bitmapLength; i++) {
                bitmap[i] = new ArrayList<>(64);
            }
            for (int i = 0; i < 65; i++) {
                findNextAvail();
            }
        }
    
        private static void findNextAvail() {
            for (int i = 0; i < bitmapLength; i++) {
                if (bitmap[i].size() == 64) {
                    continue;
                }
                for (int j = 0; i < 64; j++) {
                if (bitmap[i].size() <= j || !bitmap[i].get(j)) {
                    if (bitmap[i].size() == j) {
                    bitmap[i].add(true);
                    } else {
                    bitmap[i].set(i, true);
                    }
                    System.out.println("id :" + (i*64 + j) + " section :" + i);
                    break;
                }
                }
                break;
            }
            System.out.println();
        }
    }

    未完侍。。。

  • 相关阅读:
    我们如何监视所有 Spring Boot 微服务?
    如何使用 Spring Boot 实现异常处理?
    如何使用 Spring Boot 实现分页和排序?
    如何集成 Spring Boot 和 ActiveMQ?
    如何实现 Spring Boot 应用程序的安全性?
    Spring Boot 中的监视器是什么?
    如何重新加载 Spring Boot 上的更改,而无需重新启动服务器?
    Spring 和 SpringBoot 有什么不同?
    Spring Boot 有哪些优点?
    如何在不使用BasePACKAGE过滤器的情况下排除程序包?
  • 原文地址:https://www.cnblogs.com/solq111/p/7152288.html
Copyright © 2011-2022 走看看