全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2024-07-08_死磕 Netty 之内存篇:再探 Netty 池化内存分配管理

您的位置:首页 >> 新闻 >> 行业资讯

死磕 Netty 之内存篇:再探 Netty 池化内存分配管理 关注公众号,“技术干货”及时!本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究! ?大家好,我是大明哥,一个专注「死磕 Java[1]」系列创作的硬核程序员。 本文已收录到我的技术网站:www.skjava.com[2]。有全网最优质的系列文章、Java 全栈技术文档以及大厂完整面经 ?本篇文章我们再来探索 Netty 的池化内存分配管理机制,其实在文章 深挖 Netty 高性能内存管理 已经讲述过一次了,但是那篇文章是基于 「jemalloc3」 版本,而这篇内容则是基于 「jemalloc4」 版本(包括后面的源码部分),也是大明哥在写这个系列之前的最新版本 「4.1.77.Final」。 ?Netty 从 4.1.45 版本开始,基于 jemalloc4.x 算法对内存模块进行重构,两者差别比较大。 ?内存规格在文章 深挖 Netty 高性能内存管理 我们知道了基于 jemalloc3 的 Netty将整个内存划分为:Tiny、Small、Normal 和 Huge 四类。其中 Tiny 为 0 ~ 512 B 之间的内存块,Small 为 512B ~ 8KB 之间的内存块,Normal 为 8KB ~ 16M 之间的内存块,Huge 则是大于 16M 的,如下图: 而基于 jemalloc4 的 Netty 则将 Tiny 去掉了,保留了 Small、Normal、Huge,内存划分如下: Small:[0-28K]Normal:(28K - 16M]Huge: 16M整体架构下图是基于 jemalloc3 的 Netty 的内存池架构图: 关于这图的详情,大明哥就不再阐述了,主要是用它来跟基于 jemalloc4 的 Netty 内存结构图对比: 从这个图可以看出,Netty 根据内存模型抽象出来了一些组件:PoolArena、PoolChunkList、PoolChunk、PoolSubpage、PoolThreadCache。 下面大明哥在这篇文章就这些组件做一个简单的概括,然后每个组件利用一篇文章来详细介绍,最后用内存分配和释放将所有组件进行一个概括总结,让你彻底掌握 Netty 的内存模块!! PoolArena 数据结构PoolArena 是外部申请内存的主要入口,Netty 借据 jemalloc 中 Arena 的设计思想,采用固定数量的多个 Arena 进行内存分配,Arena 的默认数量通常为 CPU 核数 * 2,也可以通过参数 io.netty.allocator.numHeapArenas 来指定,计算规则如下: final int defaultMinNumArena = NettyRuntime.availableProcessors() * 2; final int defaultChunkSize = DEFAULT_PAGE_SIZE DEFAULT_MAX_ORDER; DEFAULT_NUM_HEAP_ARENA = Math.max(0, SystemPropertyUtil.getInt( "io.netty.allocator.numHeapArenas", (int) Math.min( defaultMinNumArena, runtime.maxMemory() / defaultChunkSize / 2 / 3))); DEFAULT_NUM_DIRECT_ARENA = Math.max(0, SystemPropertyUtil.getInt( "io.netty.allocator.numDirectArenas", (int) Math.min( defaultMinNumArena, PlatformDependent.maxDirectMemory() / defaultChunkSize / 2 / 3))); 线程在首次申请分配内存时,会通过 round-robin 的方式轮询 PoolArena 数组,选择一个固定的 PoolArena ,然后在该线程整个生命周期内就只会与该 PoolArena 打交道,所以每个线程都会保存对应的 PoolArena 信息,从而提高访问效率。 每个线程都会有一个 DirectPoolArena 和一个 HeapArena。 下面是 PoolArena 的数据结构: abstract class PoolArena extends SizeClasses implements PoolArenaMetric { static final boolean HAS_UNSAFE = PlatformDependent.hasUnsafe(); // 内存规格 enum SizeClass { Small, Normal } // 所属分配器 final PooledByteBufAllocator parent; final int numSmallSubpagePools; final int directMemoryCacheAlignment; private final PoolSubpage[] smallSubpagePools; private final PoolChunkList q050; private final PoolChunkList q025; private final PoolChunkList q000; private final PoolChunkList qInit; private final PoolChunkList q075; private final PoolChunkList q100; // 省略代码 } 图例如下: 一个 PoolArena 包含了一个 PoolSubpage[] smallSubpagePools 数组和 6 个 PoolChunkList: smallSubpagePools 数组用于存放 Small Subpage类型的内存块。6 个 PoolChunkList 用于存放使用率不同的 PoolChunk,6 个 PoolChunkList 构成一个双向循环链表。6 个 PoolChunkList 内存使用情况如下: 每个 PoolChunk 会更加内存使用率的变化在这 6 个 PoolChunkList 来回移动。 PoolChunkList 数据结构PoolChunkList 管理着多个 PoolChunk,多个使用率相同的 PoolChunk 通过双向链表的方式构建成一个 PoolChunkList,如下: 其定义如下: final class PoolChunkList implements PoolChunkListMetric { // 所属 PoolArena private final PoolArena arena; private final PoolChunkList nextList; // 最小内存使用率 private final int minUsage; // 最大内存使用率 private final int maxUsage; private final int maxCapacity; private PoolChunk head; private final int freeMinThreshold; private final int freeMaxThreshold; // 省略代码 } 每个 PoolChunkList 都有两个内存使用率的属性:minUsage 和 maxUsage。当 PoolChunk 进行内存分配时,如果内存使用率超过 maxUsage,则从当前的 PoolChunkList 中移除,并添加到下一个 PoolChunkList 中。同时,随着内存的释放,PoolChunk 的内存使用率就会减少,直到小于 minUsage ,则从当前的 PoolChunkList 中移除,并添加到上一个 PoolChunkList 中。 PoolChunk 数据结构PoolChunk 是 Netty 真正分配内存的地方,一个 PoolChunk 代表 Netty 内存池中一整块的内存,也是 Netty 内存池向 Java 虚拟机申请和释放的最小单位。其定义如下: final class PoolChunk implements PoolChunkMetric { private static final int SIZE_BIT_LENGTH = 15; private static final int INUSED_BIT_LENGTH = 1; private static final int SUBPAGE_BIT_LENGTH = 1; private static final int BITMAP_IDX_BIT_LENGTH = 32; static final int IS_SUBPAGE_SHIFT = BITMAP_IDX_BIT_LENGTH; static final int IS_USED_SHIFT = SUBPAGE_BIT_LENGTH + IS_SUBPAGE_SHIFT; static final int SIZE_SHIFT = INUSED_BIT_LENGTH + IS_USED_SHIFT; static final int RUN_OFFSET_SHIFT = SIZE_BIT_LENGTH + SIZE_SHIFT; // 所属 PoolArena final PoolArena arena; final Object base; // 存储的数据 final T memory; // 是否池化 final boolean unpooled; /** * 存储的是有用的run中的第一个和最后一个Page的句柄 */ private final LongLongHashMap runsAvailMap; /** * 管理 PoolChunk 的所有的 Run */ private final LongPriorityQueue[] runsAvail; /** * 管理 PoolChunk 中所有的 PoolSubpage */ private final PoolSubpage[] subpages; private final LongCounter pinnedBytes = PlatformDependent.newLongCounter(); // 一个 page 的大小 private final int pageSize; private final int pageShifts; private final int chunkSize; // 主要是对PooledByteBuf中频繁创建的ByteBuffer进行缓存,以避免由于频繁创建的对象导致频繁的GC private final DequeByteBuffer cachedNioBuffers; int freeBytes; // 所属 PoolChunkList PoolChunkList parent; // 后置节点 PoolChunk prev; // 前置节点 PoolChunk next; // 省略代码 } PoolChunk 的数据结构比较复杂,其结构图如下: 一个 PoolChunk 由三个部分构成: 「Run」:一个 Run 由若干个 Page 组成,Page 是 PoolChunk 的分配的最小单位。「Subpage」:用于分配 Subpage ,Subpage 的大小为 16B ~ 28K。「free」:空闲部分,待分配内存PoolChunk 还有三个很重要的属性: 「runsAvailMap」:它存储的是有用的 Run 中的第一个和最后一个 Page 的句柄。它是 runOffset → handle 之间的键值对。「runsAvail」:用于管理 PoolChunk 的所有的 Run,它是一个优先队列,每一个队列都管理着相同大小的 Run。subPages:用于管理 PoolChunk 中所有的 PoolSubpage。PoolSubpage 数据结构PoolSubpage 用于分配 Small Subpage,其定义如下: final class PoolSubpage implements PoolSubpageMetric { // 所属 PoolChunk final PoolChunk chunk; // 每块内存的大小 final int elemSize; // 页面偏移量 private final int pageShifts; // PoolSubpage 在 PoolChunk 中 memory 的偏移量 private final int runOffset; // Run 的大小 private final int runSize; // 每一小块内存的状态 private final long[] bitmap; // 前置节点 PoolSubpage prev; // 后置节点 PoolSubpage next; boolean doNotDestroy; // 最多可以存放多少小内存块 private int maxNumElems; private int bitmapLength; private int nextAvail; private int numAvail; // 省略代码 } 其结构如下: 由于篇幅和内容问题,本篇文章就结束了,后面大明哥就这些组件做详细的介绍!! Reference[1]https://link.juejin.cn/?target=https%3A%2F%2Fwww.skjava.com%2Fsike-java: https://link.juejin.cn/?target=https%3A%2F%2Fwww.skjava.com%2Fsike-java [2]https://link.juejin.cn/?target=https%3A%2F%2Fskjava.com%2F: https://link.juejin.cn/?target=https%3A%2F%2Fskjava.com%2F 关注公众号,“技术干货”及时! 阅读原文

上一篇:2017-05-03_说出来怕你不信,高露洁卖过牛肉宽面条 下一篇:2020-06-22_人与AI能相爱吗?

TAG标签:

20
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价