Netty内存管理--内存分配器PooledByteBufAllocator

一、写在前面

到这里想必你已了解了Netty内存规格化, PoolChunk、PoolSubPage、PoolArena3个关键的类。本篇想聊聊直接使用的内存分配器PooledByteBufAllocator。虽然背后还是由前面3个关键类做支持, 但是PooledByteBufAllocator基于实际场景做了一些额外的优化。本文重点关注在优化和支持优化的相关调整。

二、实际场景

问题 对策
虽然PoolArena可以管理PoolChunk和PoolSubPage, 但并发场景下, 共享对象如何解决线程间竞争问题? 创建线程数量相等的PoolArena, 每个线程绑定自己的PoolArena尽可能消除竞争
业务场景中的小对象(比如IM系统中的一条文字消息大多小于8K)其数量非常多, 频繁分配和回收浪费CPU? 回收阶段延迟回收, 尽可能重用已分配内存而不是申请新内存

1. 尽可能消除竞争

  1. 鉴于Netty本身是网络通信库, 其内存管理面向网络IO场景。内存分配器最终被EventLoop结合, 实现高效的IO。默认的EventLoop数量是CPU核心数*2, 因此默认的分配器会创建对等数量的PoolArena。
  2. 前面已经聊过Netty中支持ThreadLocal优化相关的FastThreadLocal和FastThreadLocalThread在线程绑定这里也会发挥作用。
  3. 至于哪个PoolArena应该和哪个Thread绑定, 这是个负载均衡问题。Netty在PoolArena中增加了线程数字段, PooledByteBufAllocator会从所有的PoolArena中选择一个绑定线程数最小的给当前线程使用。
  4. 由于实际向OS申请空间是由线程申请触发, 因此最终创建的对象更多了, 但不会立刻占用太多内存。

2. 延迟回收(分配复用)

  1. PoolThreadCache作为PoolThreadLocalCache的实际内容, 其中支持对已分配的Normal和Small分配做缓存, 并在超过最大数量时释放部分缓存内容。
  2. 由于存在cache, 因此在后续的分配和回收中都需要增加cache维护相关逻辑, 相关的PoolChunk,PoolSubPage和PoolArena的代码中都可以看到PoolThreadCache参数;

三、逻辑调整

分配逻辑调整

  1. 分配空间前先尝试获得线程绑定的PoolThreadCache;
  2. 获得PoolThreadCache关联的PoolArena, 委托PoolArena完成实际的分配(PoolArean更下层的委托此处不再过多讨论);
  3. 将最终得到的PooledByteBuf关联到PoolThreadCache;

回收逻辑调整

  1. 回收空间时, 第一步缓存到关联的PoolThreadCache中, 具体对象为MemoryRegionCache;
  2. 如果由于超过最大容量缓存失败则释放掉对象本身, 空间则委托给PoolArena来释放(PoolArean更下层的委托此处不再过多讨论);

四、小结

以上就是PooledByteBufAllocator解决并发环境下的共享和性能问题过程, 基本思路是消除竞争和空间复用, 可以作为工程中类似问题的一个参考。

你可能感兴趣的:(Netty,java,jvm,开发语言)