Netty的内存管理,看完还不清楚,那我就哭给你看

国内新闻 浏览(1431)

在学习Netty时,ByteBuf随处可见,但如何有效地分配ByteBuf仍然非常复杂。 Netty的汇集内存分配仍然很困难。很多人研究过它,但它仍处于迷雾中。本文主要是解释:Netty分配池外堆内存详细信息,期待让您理解!

为了更好地表达,我在文章中画了至少6个小时的画面,画面不熟悉,并且还强调了一些细节。

ByteBuf重要性

ByteBuf一直存在于Netty中,读写必备! ByteBuf是Netty的数据容器,ByteBuf的高效分配至关重要!

7564501-ccc501f3c290be52

Netty从套接字

中读取数据

7564501-1f4882795b776623

Netty已准备好将数据写入套接字

7564501-7b361120ae4a826d

从这里我们可以看到,在将数据写入套接字之前,它将判断它是否是堆外内存。如果它没有构建一个directbuffer对象,详细信息如下:

7564501-037b9344515fd9de

7564501-15c8b1f562aab452

所以本文主要是解释一下:Netty分配池外堆内存的细节,实际上,分配堆内存的细节是相似的。

为什么堆外的内存不必转移到外部存储器?为什么我加上这个判断?我以前不明白。田天和狄生之间进行了讨论,讨论很清楚,后续写了。

概述

7564501-087b3a084677f7ee

这一次,主要讨论的是汇集内存的分配。 目。

它提供外部使用的操作api:

7564501-eddc7781c1c060c1

当Netty发送数据时,它将确定它是否是堆外内存。如果没有封装:

7564501-99fade0c3e1e1287

我们这里所有人都举了本文为分配池外堆内存的例子。池化堆内存分配过程几乎相同。

我们来看看分配示例演示:

7564501-16b1c3353426292f

我们将基于这个简单的演示进行分析。

操作入门级

PooledByteBufAllocator的初始化:

7564501-cdd605f08b02a43a

输入后,您可以看到核心类的初始化操作:

7564501-c4f9b5b49e532cbe

7564501-cf1416617e535b3c

7564501-d476341460a1cb15

分配理论是jemalloc,可以理解为jemalloc实现的java版本。

PoolThreadCache

7564501-1d558394c1ddcc08

通过上图可以清楚地理解PoolThreadCache的主要数据结构。

一开始,这些缓存没有任何价值。只有在调用免费版本时(将在后续的内存版本中进行说明),先前分配的内存大小才会放在缓存的队列中。实际上,每次进行分配。当你第一次看缓存时,如果有直接返回,没有正常的分发过程(内存分配会解释)。

我们来看看PoolArena directArena的内容:

7564501-be74dd847a460033

我们来看看PoolArena结构。

PoolArena

7564501-78fbf9ed19c2e403

通过下图可以清楚地看到PoolArena的主要数据结构。

7564501-0ceb8a75f8255b7a

在PoolArena中,与PoolChunkList和PoolSubpage对应的结构是PoolChunk和PoolSubpage。让我们仔细看看这两件作品。

PoolChunk

PoolChunkList和PoolSubpage是第一次是默认值。您需要添加一个新的Chunk。默认情况下,Chunk为16M。内部结构是一个完整的二叉树,共有4096个节点和2048个叶子节点(每个叶子节点是一个页面,即8k)。非叶子节点的内存大小等于左子树内存大小加上右子树内存大小。

完整的二叉树结构如下:

7564501-e0598b9fc12fa264

这个完整的二叉树由java中的数组表示。

唯一需要注意的是下标从1开始而不是0。

7564501-cf40dc706f78bbb8

初始化后,depthMap的值不会更改,并且memoryMap的值随节点分配而变化。

7564501-42147818fca9bbd6

如果价值太高,则不会截图。这意味着上面的完整二叉树由数组表示,但该值不是节点的下标而是存储树的深度。

depthMap数组的值为0,这意味着可以分配16M空间。如果为1,则表示可以分配8M。如果是2,则可以分配4M。如果是3,则可以分配2M ...如果是11,则可以分配。 8k空间。

如果已分配节点,请将其设置为12.

您如何确定需要深入分配的大小?

如果要分配的存储器被归一化到小于8k,则可以将其分配到8k以上(即,深度为11)。

如果它是8k或大于8k,您可以通过以下代码找到深度:

Int d=maxOrder - (log2(normCapacity) - pageShifts);

知道深度后,您如何找到该节点?

7564501-e0a0d29464e5b71f

找到节点后,首先显示要占用的节点,然后更新父节点的父节点的父节点...

7564501-c656a8cee15a6a5d

SubpagePool

7564501-529a1a251dfb0147

上图是关于SubpagePool的内存结构。当我们分配页面时,我们知道它是否根据memoryMap的值进行分配。如果是subpagePool?

subpagePool分为两类:tinySubpagePools和smallSubpagePools。尺寸也适用于上图。每个类都是固定大小的。如果分配256b的大小,则一页是8k,8 * 1024/256=32块。那么你怎么说仍然分配?

私人最终的长[]位图;

由于long占用的字节数是64,我们这里只需要表示32,所以我们可以使用long。二进制的每个位表示它已被使用,0表示它尚未被使用。

7564501-ebc44964719c25c2

由于子页面不仅需要在该节点上找到完整的二叉树,因此您需要知道前几个和前几个,所以它更复杂:

7564501-8b8ab76f1c6495d3

子页面的前32位由long的前32位表示,完整二叉树的节点的最后32位是完美的。

分配核心

目:ByteBuf byteBuf=alloc.directBuffer(256);

跟进代码:

7564501-cf6443387b761490

让我们看看:PooledByteBuf buf=newByteBuf(maxCapacity);

构建PooledByteBuf对象。最后,返回pooledbytebuf对象。

让我们看看类继承结构:

0×2536个

all bytebuf bytebuf=alloc.directbuffer(256);这句话没问题,不会报告错误。

让我们来看看Newbytebuf(MaxCapacity)的详细信息:

0×2537个

这里我们使用了netty添加的回收器对象池技术。回收器的设计也很精致,后续可以写一篇专门的回收器文章,今天不是重点,我们只需要知道,由于分配一个polledbytebuf对象的成本,如果您需要经常使用polledbytebuf对象,以及性能要求,那么采购订单OLing技术是一个很好的选择(比如我们以前使用的线程池和数据库连接池)是相似的。池技术在一定程度上降低了频繁创建对象导致的性能开销。实际上,这种类似的想法很常见(比如查询数据库的成本很高,缓存是redis,这个想法是一样的),在本文的后续工作中,您也可以理解poolThreadCache。

pooledbytebuf buf=newbytebuf(maxcapacity);仅获得初始对象。

分配的核心是:分配(cache、buf、reqcapacity);

0×2538个

首先尝试一步分配,根据不同的类型定位不同的缓存,如果有分配直接返回。

如果无法分配1步,请执行上述2个步骤。

分配详细信息的2个步骤:查看需要分配的页面或子页面类型,如果子页面基于tinySubpagePools还是smallSubpagePools,找到相应的插槽,查看链表中是否有PoolSubpage,如果有任何分配修改标记退出,如果你不需要先分配一个页面,根据块表查看是否有合适的,如果有合适的话,则在这些已经存在的块上分配一个页面(分配)页面也是如此)))

然后,根据分配的页面,分配请求大小(因为页面可以存储大量相同的大小),它需要使用长位标记,指示位置已分配,以及完整的父值二叉树被修改,分配结束。如果没有块,则需要在分配新块后重复上述步骤。

发布核心

目:byteBuf.release();

跟进代码:

7564501-1af52fbe80e0d7f8

7564501-a4b5376fe888198d

7564501-ce58deeb6b47a300

通过这段代码,我们将其放入相应的队列中:

7564501-1f6324afc7af3def

缓存到相应的Cache队列中。

已经工作了一到五年的Java工程师朋友加入了我在Java的个人粉丝群:该库提供免费的Java架构学习材料(高可用性,高并发性,高性能和分布式,Jvm性能调优)优秀,Spring源代码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等知识点的信息结构使用他们的每一分钟和每一秒来学习提高自己,不要使用'没时间'掩盖你自己懒惰的想法!趁年轻,努力工作,给自己一个未来的账户!