我对Netty的直接内存管理存在疑问。
我通过以下方式创建一个直接缓冲区:
@GetMapping("/new/buffer/netty/unpool")
@Synchronized
public void newBufferNetty() {
UnpooledByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT;
ByteBuf buffer = allocator.buffer(100 * 1024 * 1024);
_myByteBufList.add(buffer);
log.info("buffer[{}] created", buffer);
}
然后我观察了top
生成的信息,我发现内存没有变化(RES,SWAP和free)。我感到很困惑,因为如果我像N ByteBuffer.allocateDirect(1024*1024*100);
一样以NIO方式进行操作,操作系统内存信息会改变。
在我调查了源代码之后,我发现NIO通过new DirectByteBuffer(cap)
创建了一个directByteBuffer,而Netty实际上是通过new DirectByteBuffer(addr, cap)
创建的。在后一种情况下,Netty没有打电话给Bits.reserve(size, cap)
,这就是我认为为什么top
没有显示任何变化的原因。
我还发现Netty使用自己的计数器 DIRECT_MEMORY_COUNTER 来跟踪它分配的直接内存。
我的问题是:
为什么Netty在分配直接内存时不会调用Bits.reserve
?
为什么Netty必须使用自己的计数器来监控直接内存的使用?
(这是让我最困惑的一个)为什么在创建Netty缓冲区时没有对os内存进行任何更改(UnpooledUnsafeNoCleanerDirectByteBuf)
非常感谢你。
答案 0 :(得分:2)
我只回答3分。前2点只能由Netty作者解释。
如前所述,Netty使用new DirectByteBuffer(addr, cap)
创建直接缓冲区。传递的内存地址是Unsafe.allocateMemory
返回的一个调用系统内存分配工具的地址。如https://en.wikipedia.org/wiki/C_dynamic_memory_allocation
在延迟内存分配方案(例如Linux操作系统中常见的那些方案)中,大型堆不一定保留等效的系统内存;它只会在第一次写入时执行此操作(非映射内存页的读取返回零)。其粒度取决于页面大小。
因此,预计top
不会反映Netty直接缓冲区分配。
相反,ByteBuffer.allocateDirect
使用Unsafe.setMemory
在使用Unsafe.allocateMemory
分配后将所有字节设置为零。这种行为在Javadoc https://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#allocateDirect(int)
新缓冲区的位置将为零,其限制将是其容量,其标记将是未定义的,并且其每个元素将初始化为零。
它解释了ByteBuffer.allocateDirect
通过top
分配的原因。请注意,仅在首次使用已分配的内存后才会增加应用程序内存使用量。