[Netty源码] ByteBufAllocator内存管理器相关问题 (十一)

news2024/10/6 20:33:10

文章目录

      • 1.ByteBufAllocator 内存管理器
      • 2.UnpooledByteBufAllocator
        • 2.1 heap内存的分配
        • 2.2 direct内存的分配
      • 3.PooledByteBufAllocator
        • 3.1 heap内存和direct内存的分配
        • 3.2 directArena分配direct内存的流程
        • 3.3 内存规格的介绍
      • 4.缓存的相关问题
        • 4.1 缓存的数据结果
        • 4.2 命中缓存的分配流程
      • 5.PoolThreadCache的相关问题
        • 5.1 Arena数据结构分析
        • 5.2 Page级别的内存分配
        • 5.3 Subpage级别的内存分配

1.ByteBufAllocator 内存管理器

ByteBuf分类:

  • pooled和unpooled
  • heap和direct
  • unsafe和非unsafe

在这里插入图片描述

在这里插入图片描述

通过不同的方法去读取到数据。

    @Override
    public ByteBuf buffer() {
        if (directByDefault) {
            return directBuffer();
        }
        return heapBuffer();
    }

堆内存分类

    @Override
    public ByteBuf heapBuffer() {
        return heapBuffer(DEFAULT_INITIAL_CAPACITY, DEFAULT_MAX_CAPACITY);
    }

    @Override
    public ByteBuf heapBuffer(int initialCapacity, int maxCapacity) {
        if (initialCapacity == 0 && maxCapacity == 0) {
            return emptyBuf;
        }
        validate(initialCapacity, maxCapacity);
        return newHeapBuffer(initialCapacity, maxCapacity);
    }

在这里插入图片描述

  • PooledByteBufAllocator: 通过在预先分配好的内存中分配数据
  • UnpooledByteBufAllocator: 从操作系统中直接分配内存数据

2.UnpooledByteBufAllocator

  • newHeapBuffer
  • newDirectBuffer

2.1 heap内存的分配

UnpooledByteBufAllocator.newHeapBuffer()

在这里插入图片描述

判断是否是Unsafe, 如果有Unsafe的辅助, 为InstrumentedUnpooledUnsafeHeapByteBuf, 如果没有则为InstrumentedUnpooledHeapByteBuf

在这里插入图片描述

在这里插入图片描述

一个是UnpooledUnsafeHeapByteBuf, 一个是UnpooledHeapByteBuf, 不过UnpooledUnsafeHeapByteBuf是继承UnpooledHeapByteBuf

在这里插入图片描述

UnpooledUnsafeHeapByteBuf

在这里插入图片描述

在这里插入图片描述

这里的UnpooledUnsafeHeapByteBufUnpooledHeapByteBuf区别在于_getByte()以及一些类似的方法, 区别在于工具类的使用上

UnpooledUnsafeHeapByteBuf

在这里插入图片描述

UnpooledHeapByteBuf

在这里插入图片描述

一个是UnsafeByteBufUtil, 一个是HeapByteBufUtil

UnsafeByteBufUtil

在这里插入图片描述

HeapByteBufUtil

在这里插入图片描述

UnsafeByteBufUtil是通过对象加偏移量的方式获取数据的, 而HeapByteBufUtil是通过数组获取数据的。前一种的效率高一些。

2.2 direct内存的分配

UnpooledByteBufAllocator.newDirectBuffer()

在这里插入图片描述

  • InstrumentedUnpooledDirectByteBuf: UnpooledDirectByteBuf
  • InstrumentedUnpooledUnsafeDirectByteBuf: UnpooledUnsafeDirectByteBuf
  • InstrumentedUnpooledUnsafeNoCleanerDirectByteBuf: UnpooledUnsafeNoCleanerDirectByteBuf, UnpooledUnsafeDirectByteBuf

在这里插入图片描述

UnpooledDirectByteBuf

在这里插入图片描述

UnpooledDirectByteBuf.setByteBuffer()

在这里插入图片描述

UnpooledUnsafeDirectByteBuf.setByteBuffer()

在这里插入图片描述

在这里插入图片描述

先是调用了子类的方法, 然后调用Unsafe拿到对应的数据。

其次是_getByte()等方法的不同, Unsafe会通过一个内存地址加偏移量获取数据, 非unsafe通过数组下标获取数据的。

3.PooledByteBufAllocator

  • newDirectBuffer
  • newHeapBuffer

3.1 heap内存和direct内存的分配

    private final PoolThreadLocalCache threadCache;

    @Override
    protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
    	// 获取线程局部缓存PoolThreadCache
        PoolThreadCache cache = threadCache.get();
        // 从局部缓存中获取到heap竞技场部分 
        PoolArena<byte[]> heapArena = cache.heapArena;

        final ByteBuf buf;
        if (heapArena != null) {
        	// 主要的进行分配的过程
            buf = heapArena.allocate(cache, initialCapacity, maxCapacity);
        } else {
            buf = PlatformDependent.hasUnsafe() ?
                    new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity) :
                    new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
        }

        return toLeakAwareBuffer(buf);
    }
  • 获取线程局部缓存PoolThreadCache, 从PoolThreadLocalCache 获取到
  • 从局部缓存中获取到heap竞技场部分PoolArena
  • 从Arena上进行内存分配

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这里的nDirectArena为2倍的cpu核数, 为的是之前创建的NioEventLoop的数量对应

在这里插入图片描述

在这里插入图片描述

PooledByteBufAllocator的结构, 每一个Thread和Area对应, 都是2倍Cpu核数

在这里插入图片描述

线程局部缓存PoolThreadCache

  • tinyCacheSize
  • smallCacheSize
  • normalCacheSize

在这里插入图片描述

3.2 directArena分配direct内存的流程

directArena.allocate(cache, initialCapacity, maxCapacity)

  1. 从对象池中获取PooledByteBuf进行复用
  2. 从缓存上进行内存分配
  3. 从内存堆中进行内存分配

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • Unsafe: PooledUnsafeDirectByteBuf
  • 非Unsafe: PooledDirectByteBuf

在这里插入图片描述

  • 轻量级对象池获取ByteBuf
  • ByteBuf复用方法

在这里插入图片描述

在这里插入图片描述

allocate()

分配内存逻辑

   private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
        final int normCapacity = normalizeCapacity(reqCapacity);
        if (isTinyOrSmall(normCapacity)) { // capacity < pageSize
            int tableIdx;
            PoolSubpage<T>[] table;
            boolean tiny = isTiny(normCapacity);
            if (tiny) { // < 512
                if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {
                    // was able to allocate out of the cache so move on
                    return;
                }
                tableIdx = tinyIdx(normCapacity);
                table = tinySubpagePools;
            } else {
                if (cache.allocateSmall(this, buf, reqCapacity, normCapacity)) {
                    // was able to allocate out of the cache so move on
                    return;
                }
                tableIdx = smallIdx(normCapacity);
                table = smallSubpagePools;
            }

            final PoolSubpage<T> head = table[tableIdx];

            /**
             * Synchronize on the head. This is needed as {@link PoolChunk#allocateSubpage(int)} and
             * {@link PoolChunk#free(long)} may modify the doubly linked list as well.
             */
            synchronized (head) {
                final PoolSubpage<T> s = head.next;
                if (s != head) {
                    assert s.doNotDestroy && s.elemSize == normCapacity;
                    long handle = s.allocate();
                    assert handle >= 0;
                    s.chunk.initBufWithSubpage(buf, null, handle, reqCapacity);
                    incTinySmallAllocation(tiny);
                    return;
                }
            }
            synchronized (this) {
                allocateNormal(buf, reqCapacity, normCapacity);
            }

            incTinySmallAllocation(tiny);
            return;
        }
        if (normCapacity <= chunkSize) {
            if (cache.allocateNormal(this, buf, reqCapacity, normCapacity)) {
                // was able to allocate out of the cache so move on
                return;
            }
            synchronized (this) {
                allocateNormal(buf, reqCapacity, normCapacity);
                ++allocationsNormal;
            }
        } else {
            // Huge allocations are never served via the cache so just call allocateHuge
            allocateHuge(buf, reqCapacity);
        }
    }

3.3 内存规格的介绍

在这里插入图片描述

  1. 0 - 512B 为tinySize, 单位为SubPage
  2. 512B - 8K 为smallSize, 单位为SubPage
  3. 8K - 16M 为normalSize, 单位为Page
  4. 16M以上 为hugeSize, 单位为Chunk

4.缓存的相关问题

4.1 缓存的数据结果

MemoryRegionCache

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • queue: chunk handler执行逻辑
  • sizeClass: tiny, small, normal
  • size: 16B, 512B, 1K, 2K, 4K, 8K, 16K, 32K

在这里插入图片描述

  • tiny的数组大小为32: 16B - 496B
  • small的数组大小为4: 512BM 1K 2K 4K
  • normal的数组大小为3: 8K 16K 32K

在这里插入图片描述

在这里插入图片描述

通过计算和传参获取到数组大小和数组的单位的大小

4.2 命中缓存的分配流程

分配内存逻辑中存在名字缓存的分配流程。

   private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
        final int normCapacity = normalizeCapacity(reqCapacity);
        if (isTinyOrSmall(normCapacity)) { // capacity < pageSize
            int tableIdx;
            PoolSubpage<T>[] table;
            boolean tiny = isTiny(normCapacity);
            if (tiny) { // < 512
            	// 命中缓存的分配流程
                if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {
                    // was able to allocate out of the cache so move on
                    return;
                }
                tableIdx = tinyIdx(normCapacity);
                table = tinySubpagePools;
            } else {
            	// 命中缓存的分配流程
                if (cache.allocateSmall(this, buf, reqCapacity, normCapacity)) {
                    // was able to allocate out of the cache so move on
                    return;
                }
                tableIdx = smallIdx(normCapacity);
                table = smallSubpagePools;
            }

            final PoolSubpage<T> head = table[tableIdx];

            /**
             * Synchronize on the head. This is needed as {@link PoolChunk#allocateSubpage(int)} and
             * {@link PoolChunk#free(long)} may modify the doubly linked list as well.
             */
            synchronized (head) {
                final PoolSubpage<T> s = head.next;
                if (s != head) {
                    assert s.doNotDestroy && s.elemSize == normCapacity;
                    long handle = s.allocate();
                    assert handle >= 0;
                    s.chunk.initBufWithSubpage(buf, null, handle, reqCapacity);
                    incTinySmallAllocation(tiny);
                    return;
                }
            }
            synchronized (this) {
                allocateNormal(buf, reqCapacity, normCapacity);
            }

            incTinySmallAllocation(tiny);
            return;
        }
        if (normCapacity <= chunkSize) {
            if (cache.allocateNormal(this, buf, reqCapacity, normCapacity)) {
                // was able to allocate out of the cache so move on
                return;
            }
            synchronized (this) {
                allocateNormal(buf, reqCapacity, normCapacity);
                ++allocationsNormal;
            }
        } else {
            // Huge allocations are never served via the cache so just call allocateHuge
            allocateHuge(buf, reqCapacity);
        }
    }

在这里插入图片描述

在这里插入图片描述

  1. 找到对应size的MemoryRegionCache
  2. 从queue中弹出一个entry给ByteBuf初始化
  3. 将弹出的entry扔到对象池进行复用

找到对应size的MemoryRegionCache

在这里插入图片描述

从queue中弹出一个entry给ByteBuf初始化

在这里插入图片描述

初始化

在这里插入图片描述

将弹出的entry扔到对象池进行复用

在这里插入图片描述

5.PoolThreadCache的相关问题

在这里插入图片描述

  1. cache: 之前的MemoryRegionCache
  2. arena: PoolArena, PoolChunkList, PoolChunk, PoolSubpage

在这里插入图片描述

5.1 Arena数据结构分析

Arena: ChunkList的双向链表 -> Chunk的双向链表

在这里插入图片描述

Chunk -> subpage[]数组
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

5.2 Page级别的内存分配

allocateNormal()

在这里插入图片描述

  1. 尝试在现有的chunk上分配
  2. 创建一个chunk进行分配
  3. 初始化pooledByteBuf: c.allocate()

创建chunk分配 PoolChunk

在这里插入图片描述

    PoolChunk(PoolArena<T> arena, T memory, int pageSize, int maxOrder, int pageShifts, int chunkSize, int offset) {
        unpooled = false;
        this.arena = arena;
        this.memory = memory;
        this.pageSize = pageSize;
        this.pageShifts = pageShifts;
        this.maxOrder = maxOrder;
        this.chunkSize = chunkSize;
        this.offset = offset;
        unusable = (byte) (maxOrder + 1);
        log2ChunkSize = log2(chunkSize);
        subpageOverflowMask = ~(pageSize - 1);
        freeBytes = chunkSize;

        assert maxOrder < 30 : "maxOrder should be < 30, but is: " + maxOrder;
        maxSubpageAllocs = 1 << maxOrder;

        memoryMap = new byte[maxSubpageAllocs << 1];
        depthMap = new byte[memoryMap.length];
        int memoryMapIndex = 1;
        for (int d = 0; d <= maxOrder; ++ d) { // move down the tree one level at a time
            int depth = 1 << d;
            for (int p = 0; p < depth; ++ p) {
                // in each level traverse left to right and set value to the depth of subtree
                memoryMap[memoryMapIndex] = (byte) d;
                depthMap[memoryMapIndex] = (byte) d;
                memoryMapIndex ++;
            }
        }

        subpages = newSubpageArray(maxSubpageAllocs);
        cachedNioBuffers = new ArrayDeque<ByteBuffer>(8);
    }

handle: 对应chunk中第几个page的第几个subpage, 就是拿到内存里的哪一个连续内存

初始化PooledByteBuf

在这里插入图片描述

在这里插入图片描述

5.3 Subpage级别的内存分配

allocateSubpage()

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 定位到Subpage对象
  • 初始化Subpage: 划分page
  • 初始化PooledByteBuf: 和page级别的内存分配一个代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/410861.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一维差分思想【算法推导、深刻思考】

797. 差分 - AcWing题库 差分本质上就是前缀和的逆运算 算法推导 其实在最开始自己去完成这个题目的时候&#xff0c;感觉好像是可以往前缀和方向靠的&#xff0c;但是一下子没有想到实现方法就无疾而终了。所以最后选择的算法就只是单纯的暴力&#xff08;虽然知道过不了&…

【操作系统复习】第5章 存储器管理

存储器的层次结构 存储层次 ➢ CPU寄存器 ➢ 主存&#xff1a;高速缓存、主存储器、磁盘缓存 ➢ 辅存&#xff1a;固定磁盘、可移动介质 层次越高&#xff0c;访问速度越快&#xff0c;价格也越高&#xff0c;存储容量也最小 寄存器和主存掉电后存储的信息不再存在&a…

2024软件工程考研之《软件工程导论》专业课复习

一、考察《软件工程导论》的学校 截止目前&#xff0c;考察《软件工程导论》的学校主要有&#xff1a; 大连理工大学887 北京航天航空大学991 北京交通大学901 河海大学846 海南大学835 新疆大学841 成都信息工程大学809 长安大学846 天津工业大学840 华东交通大学837 大连交通…

采购招投标系统-高效管控招采流程-降低采购成本

项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&#xff0c;以及…

软考证书找工作有用吗?软考找工作用处大吗

软考证书是衡量IT技术人才能力的一种重要评价标准。 一、软考高级证书对找工作的帮助 1. 竞争力增强 软考高级证书具有一定难度和较高的专业技能要求&#xff0c;拥有该证书的人的技术水平和专业能力会得到认可和尊重&#xff0c;从而增强求职者的竞争力。 2. 拓宽职业发展…

防火墙NAT实验,双机热备实验

目录 NAT防火墙基础实验 源地址转换 服务器映射 域内双向NAT 域间双向NAT 双机热备基础实验 主备备份 负载分担 NAT防火墙基础实验 实验拓扑&#xff1a; 1.进入防火墙图形化页面进行配置 接口列表的配置 源地址转换 企业内部网络访问外部网络&#xff0c;进行源地…

如何用nodejs构造一个网站爬虫

爬虫是个什么东西 英文spider&#xff0c;网络爬虫&#xff08;又称为网页蜘蛛&#xff0c;网络机器人&#xff0c;在FOAF社区中间&#xff0c;更经常的称为网页追逐者&#xff09;&#xff0c;是一种按照一定的规则&#xff0c;自动地抓取万维网信息的程序或者脚本。另外一些…

干货分享 | 采购没“云”和有云的区别有哪些?

多年前&#xff0c;提起“云”这个词&#xff0c;很多人还是“不知所云”。 但如今&#xff0c;大众对“云”的了解和认可程度也越来越高&#xff0c;尽情享受着“云”带来的便利。 通过“云”&#xff0c;可以随时随地畅听海量音乐、进行网购、访问云盘的照片和视频、在云端创…

【数据分析】——分析方法

上司要你帮忙看看公司最近的网站运营情况怎么样&#xff1f; 公司最近的网站运营情况&#xff1f;这个问题太宽泛了&#xff0c;你得要知道上司的明确需求 问题1:boss,你是想看看公司网站具体哪方面的问题&#xff1f; 回答&#xff1a;公司最近销售不太好&#xff0c;订单转…

Kubernetes 多集群网络方案系列 1 -- Submariner 介绍

Submariner 是一个完全开源的项目&#xff0c;可以帮助我们在不同的 Kubernetes 集群之间&#xff08;无论是在本地还是云端&#xff09;实现网络通信。Submariner 有以下功能&#xff1a; 跨集群的 L3 连接跨集群的服务发现Globalnet 支持 CIDR 重叠提供命令行工具 subctl 简…

MappedByteBuffer 详解(图解+秒懂+史上最全)

背景&#xff1a; 在尼恩视频版本里&#xff0c;从架构师视角&#xff0c;尼恩为大家彻底介绍 rocketmq 高可用、高并发中间件的原理与实操。 给大家底层的解读清楚 rocketmq 架构设计、源码设计、工业级高可用实操&#xff0c;含好多复杂度非常高、又非常核心的概念&#xff…

Python 小型项目大全 11~15

十一、标题党生成器 原文&#xff1a;http://inventwithpython.com/bigbookpython/project11.html 我们的网站需要欺骗人们去看广告&#xff01;但是想出有创意的原创内容太难了。幸运的是&#xff0c;有了标题党生成器&#xff0c;我们可以让一台计算机产生数百万个令人发指的…

在CentOS 7.5上用离线压缩包安装Python-3.9.10的过程记录

项目的需要&#xff0c;需要在CentOS 7.5上去部署一个Python 3.9.10的安装环境。 值得注意的是在CentOS 7.5系统中&#xff0c;默认安装了 Python 2.7.x。 Python 2.7.x 是许多系统工具和应用程序所依赖的版本&#xff0c;因此它被包括在 CentOS 7.5 发行版中。如果在 CentOS …

太全面了,数据治理8大核心模块建设参考

数据治理是一个去中心化、多元参与的系统工程。一个全面且明确的数据治理体系&#xff0c;可以帮助组织构建生态式、协同化治理路径&#xff0c;最大化地提升整体数据质量&#xff0c;实现数据战略&#xff0c;激活新型生产力。 本文以元数据、数据标准、主数据、数据交换、数…

Mybatis-Plus批量添加或修改数据的三种方式

SpringBoot批量添加或修改数据的三种方式 方式一.Mybatis-plus 提供的 saveOrUpdateBatch 提供的方法 是遍历每一个元素&#xff0c;判断主键是否存在&#xff0c;如果存在则做更新&#xff0c;不存在添加 方式二.分组数据再批量添加或修改 先获取表中所有的主键 &#xff0…

6D姿态检测

3D相机—结构光相机 结构光&#xff0c;英文叫做 Structured light&#xff0c;其原理是基本原理是&#xff0c;通过近红外激光器&#xff0c;将具有一定结构特征的光线投射到被拍摄物体上&#xff0c;再由专门的红外摄像头进行采集。这种具备一定结构的光线&#xff0c;会因被…

如何在TikTok视频描述中提高用户参与度

鑫优尚电子商务&#xff1a;TikTok视频描述&#xff08;包括话题标签&#xff09;有150个字符的限制&#xff0c;因此卖家需要合理撰写出有趣且有实际意义的视频描述。可尝试将描述保持在140个字符以内&#xff0c;将最重要的信息放在前面&#xff0c;并通过多次修改文案以排除…

蚂蚁4.11笔试

文章目录前言一、红蓝格子填字母【蚂蚁4.11笔试第三题】解法一&#xff1a;二分解法解法二&#xff1a;模拟二、桌上弹球游戏【蚂蚁4.11笔试第二题】每日一题day82&#xff1a;困于环中的机器人&#xff08;力扣1041&#xff09;前言 1、红蓝格子填字母 2、桌上弹球游戏 3、困…

12 个好用且不花钱的网络监控工具

导读要让一个多级机构运行良好而且平稳的话&#xff0c;一个非常艰巨重大的任务就是做好网络管理。每个机构都配备专门的人员&#xff0c;即网络分析师&#xff0c;来进行网络管理。他们 使用了 许多工具来监视网络的运行状况&#xff0c;并查看网络流量的上升和下降状况。他们…

机器学习 03 K-近邻算法

目录 一、K-近邻算法 1.1 K-近邻算法(KNN)概念 1.1.1 概念理解举例 1.2 KNN算法流程总结 1.3 K值的选择 1.3.1 举例说明 1.4 kd树 1.4.1 KD树原理 1.4.2 树的建立 1.5 最近领域的搜索 k近邻算法优缺点 二、距离度量 2.1 距离公式的基本性质 2.2 常见的距离 2.2.…