PooledByteBuf内存池

转载自:http://blog.csdn.net/youaremoon/article/details/47910971
              http://blog.csdn.net/youaremoon/article/details/47984409
              http://blog.csdn.net/youaremoon/article/details/48085591
              http://blog.csdn.net/youaremoon/article/details/48184429
              http://blog.csdn.net/youaremoon/article/details/50042373
              http://blog.csdn.net/youaremoon/article/details/50054387

       从netty 4开始,netty加入了内存池管理,采用内存池管理比普通的new ByteBuf性能提高了数十倍。
       首先介绍PoolChunk, 该类主要负责内存块的分配与回收,首先来看看两个重要的术语:
          page: 可以分配的最小的内存块单位。
          chunk: 一堆page的集合。
       下面一张图直观的表述了PoolChunk是如何管理内存的:
       
       上图中是一个默认大小的chunk, 由2048个page组成了一个chunk,一个page的大小为8192, chunk之上有11层节点,最后一层节点数与page数量相等。每次内存分配需要保证内存的连续性,这样才能简单的操作分配到的内存,因此这里构造了一颗完整的平衡二叉树,所有子节点的管理的内存也属于其父节点。如果想获取一个8K的内存,则只需在第11层找一个可用节点即可,而如果需要16K的数据,则需要在第10层找一个可用节点,因为需要两个第11层节点。如果一个节点存在一个已经被分配的子节点,则该节点不能被分配,例如需要16K内存,这个时候id=2048的节点已经被分配,id=2049的节点未分配,就不能直接分配1024这个节点,因为这个节点下的内存只有8K了。
       通过上面这个树结构,可以看到每次内存分配都是8K*(2^n), 比如需要24K内存时,实际上会申请到一块32K的内存。为了分配一个大小为chunkSize/(2^i)的内存段,需要在深度为i的层从左开始查找可用节点。如想分配16K的内存,chunkSize = 16M( 2048个page * 8K ), 则i=10, 需要从第10层找一个空闲的节点分配内存。

       负责内存分配的PoolChunk类,它最小的分配单位为page, 而默认的page size为8K。在实际的应用中,会存在很多小块内存的分配,如果小块内存也占用一个page明显很浪费,针对这种情况,可以将8K的page拆成更小的块,这已经超出chunk的管理范围了,这个时候就出现了PoolSubpage, 其实PoolSubpage做的事情和PoolChunk做的事情类似,只是PoolSubpage管理的是更小的一段内存。
       
       如上图,PoolSubpage将chunk中的一个page再次划分,分成相同大小的N份,这里暂且叫Element,通过对每一个Element的标记与清理标记来进行内存的分配与释放。

       介绍了PoolChunk以及针对page的更细粒度的PoolSubpage,其实在chunk的上层还有一个管理类:PoolChunkList,PoolChunkList负责管理多个chunk的生命周期,在此基础上对内存分配进行进一步的优化。
       PoolChunkList主要是为了提高内存分配的效率,每个list中包含多个chunk,而多个list又可以形成一个大的link list,在进行内存分配时,先从比较靠前的list中分配内存,这样分配到的几率更大。在高峰期申请过多的内存后,随着流量下降慢慢的释放掉多余内存,形成一个良性的循环。下图是上述三个类的层次结构:
       
 
已经讲到了内存池中的几个重要的类:
       1、PoolChunk:维护一段连续内存,并负责内存块分配与回收,其中比较重要的两个概念:page:可分配的最小内存块单位;chunk:page的集合;
       2、PoolSubpage:将page分为更小的块进行维护;
       3、PoolChunkList:维护多个PoolChunk的生命周期。
       多个PoolChunkList也会形成一个list,方便内存的管理。最终由PoolArena对这一系列类进行管理,PoolArena本身是一个抽象类,其子类为HeapArena和DirectArena,对应堆内存(heap buffer)和堆外内存(direct buffer),除了操作的内存(byte[]和ByteBuffer)不同外两个类完全一致。

内存池内存分配流程:
       1、ByteBufAllocator 准备申请一块内存;
       2、尝试从PoolThreadCache中获取可用内存,如果成功则完成此次分配,否则继续往下走,注意后面的内存分配都会加锁;
       3、如果是小块(可配置该值)内存分配,则尝试从PoolArena中缓存的PoolSubpage中获取内存,如果成功则完成此次分配;
       4、如果是普通大小的内存分配,则从PoolChunkList中查找可用PoolChunk并进行内存分配,如果没有可用的PoolChunk则创建一个并加入到PoolChunkList中,完成此次内存分配;
       5、如果是大块(大于一个chunk的大小)内存分配,则直接分配内存而不用内存池的方式;
       6、内存使用完成后进行释放,释放的时候首先判断是否和分配的时候是同一个线程,如果是则尝试将其放入PoolThreadCache,这块内存将会在下一次同一个线程申请内存时使用,即前面的步骤2;
       7、如果不是同一个线程,则回收至chunk中,此时chunk中的内存使用率会发生变化,可能导致该chunk在不同的PoolChunkList中移动,或者整个chunk回收(chunk在q000上,且其分配的所有内存被释放);同时如果释放的是小块内存(与步骤3中描述的内存相同),会尝试将小块内存前置到PoolArena中,这里操作成功了,步骤3的操作中才可能成功。
       在PoolThreadCache中分了tinySubPageHeapCaches、smallSubPageHeapCaches、normalSubPageHeapCaches三个数组,对应于tiny\small\normal在内存分配上的不同(tiny和small使用subpage,normal使用page)。
       到此,netty内存池相关介绍已经完,netty就是实现了两个比较经典的分配策略,buddy allocation(见PoolChunk)和jemalloc(有一定改动,PooledByteBufAllocator+PoolArena+PoolChunk+PoolThreadCache),所以如果想了解更新的信息,可以按照上面两个关键词搜索,或则看转载的原文。

netty内存池可调优参数

参数名说明默认值
io.netty.allocator.pageSizepage的大小8192
io.netty.allocator.maxOrder一个chunk的大小=pageSize << maxOrder11
io.netty.allocator.numHeapArenasheap arena的个数min(cpu核数,maxMemory/chunkSize/6),一般来说会=cpu核数
io.netty.allocator.numDirectArenasdirect arena的个数min(cpu核数,directMemory/chunkSize/6),一般来说会=cpu核数
io.netty.allocator.tinyCacheSizePoolThreadCache中tiny cache每个MemoryRegionCache中的Entry个数512
io.netty.allocator.smallCacheSizePoolThreadCache中small cache每个MemoryRegionCache中的Entry个数256
io.netty.allocator.normalCacheSizePoolThreadCache中normal cache每个MemoryRegionCache中的Entry个数64
io.netty.allocator.maxCachedBufferCapacityPoolThreadCache中normal cache数组长度32 * 1024
io.netty.allocator.cacheTrimIntervalPoolThreadCache中的cache收缩阈值,每隔该值次数,会进行一次收缩8192
io.netty.allocator.typeallocator类型,如果不使用内存池,则设置为unpooledpooled
io.netty.noUnsafe是否关闭direct bufferfalse
io.netty.leakDetectionLevel内存泄露检测级别SIMPLE

 


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以通过以下代码创建 PooledByteBuf: ``` ByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT; ByteBuf buf = allocator.buffer(initialCapacity); ``` 其中,initialCapacity 是初始容量。PooledByteBuf 是使用池化技术实现的 ByteBuf,可以重复利用已经分配的内存,从而提高内存使用效率和性能。 ### 回答2: PooledByteBuf是Netty框架中的一个类,用于管理字节缓冲区的池化分配和释放。它能够提高内存分配和释放的性能,并减少了对垃圾回收器的负担。 在Netty中,我们可以通过以下步骤来创建PooledByteBuf: 1. 首先,我们需要获取一个ByteBufAllocator对象,它负责分配字节缓冲区。可以通过Channel或者Netty的静态方法Unpooled来获取一个ByteBufAllocator对象。 2. 然后,我们可以调用ByteBufAllocator的buffer()或者directBuffer()方法来创建一个新的字节缓冲区。这两种方法的区别在于buffer()方法创建的是堆缓冲区,而directBuffer()方法创建的是直接缓冲区。 3. 接下来,我们可以通过PooledByteBufAllocator.DEFAULT来获取一个PooledByteBufAllocator对象,它是一个单例对象,用于管理池化的字节缓冲区。 4. 最后,我们可以通过调用PooledByteBufAllocator的directBuffer()或者heapBuffer()方法来创建一个PooledByteBuf对象。这两个方法分别用于创建直接缓冲区和堆缓冲区的池化字节缓冲区。 创建PooledByteBuf的示例代码如下: ``` ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT; PooledByteBufAllocator pooledAllocator = PooledByteBufAllocator.DEFAULT; // 创建直接缓冲区的PooledByteBuf对象 PooledByteBuf directBuffer = pooledAllocator.directBuffer(); // 创建堆缓冲区的PooledByteBuf对象 PooledByteBuf heapBuffer = pooledAllocator.heapBuffer(); ``` 通过以上步骤,我们可以创建一个PooledByteBuf对象,并且可以使用它来读取和写入数据。在使用完毕后,我们需要调用PooledByteBuf的release()方法来释放PooledByteBuf对象,将它归还给池化的对象池,以便其他代码可以重用它。同时,我们还可以通过调用refCnt()方法来获取PooledByteBuf对象的引用计数,以便在释放之前进行引用计数的检查。 ### 回答3: PooledByteBuf是Netty中的一个类,它是通过内存池来管理字节缓冲区的分配和释放。它的创建过程如下所示: 1. 首先,我们需要获取一个PooledByteBufAllocator实例,这是Netty内置的一个基于内存池的字节缓冲区分配器。 2. 然后,通过调用PooledByteBufAllocator的directBuffer()或heapBuffer()方法来创建一个PooledByteBuf实例。 - 如果使用directBuffer()方法创建,将会在直接内存中分配字节缓冲区,适用于需要高性能的场景;但是由于直接内存的分配和释放比堆内存略慢,因此适用于较大的数据包。 - 如果使用heapBuffer()方法创建,将会在堆内存中分配字节缓冲区,适用于需要小数据包和快速分配的场景;但是由于存在堆内存与直接内存之间的数据拷贝,因此性能会略低。 3. 创建PooledByteBuf实例时,需要传入两个参数,分别是初始容量和最大容量。 - 初始容量是指刚刚创建时字节缓冲区的初始大小,可以根据实际情况进行设置。 - 最大容量是对字节缓冲区大小的限制,当超过该大小时,将会触发扩容或抛出异常。 4. 创建PooledByteBuf实例后,可以通过调用其各种方法,如writeByte()、readByte()等,对字节缓冲区进行读写操作。 5. 当使用完成后,需要调用PooledByteBuf的release()方法来释放该实例,以便将其返还给内存池进行重复利用。 总之,通过PooledByteBuf的创建和使用,可以有效地管理字节缓冲区的分配和释放,提高系统的性能和资源利用率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值