RecvByteBufAllocator
@Override
public ByteBuf allocate(ByteBufAllocator alloc) {
return alloc.ioBuffer(guess());
}
申请buffer的initialCapacity是通过guess()方法得到的。
guess实现在AdaptiveRecvByteBufAllocator
中,这个类通过反馈的方式调整下次申请的buffer大小。调整的大小数组是前32小的值都是每次增加16byte, 达到512byte后按照每次乘二的方式。如果实际读到的数据小于上一个数组位置的值,下次申请回收缩,相反大于后面的数组位置时也会进行增加,不过增加的间隔是4,也就是出现16M的情况之前的申请大小是1M并且实际读到的数据大于等于2M。
上述日志表明,当前已经申请的DirectMemory加上将要申请的16M左右DirectMemory超过了1.8G的DirectMemoryLimit。
那netty中使用的DirectByteBuffer什么时候进行释放呢,需要仔细研究下netty代码了。
可以在PlatformDependent
中看到与incrementMemoryCounter
相对的还有decrementMemoryCounter
方法负责减少DIRECT_MEMORY_COUNTER的值,其中freeDirectNoCleaner
方法调用了UNSAFE.freeMemory(address)
进行直接内存的释放, 跟踪调用链找到了PoolArena
的free和reallocate方法,reallocate会在PooledByteBuf中调用capacity进行。
现在通过设置-Dio.netty.maxDirectMemory=0并增加-Dio.netty.leakDetectionLevel=advanced继续观察。
附一段查看JDK8的DirectMemory的程序 https://gist.github.com/liuzhengyang/a0d25510d706c6f4c0805b367ad502da ,使用方式见https://gist.github.com/rednaxelafx/1593521