-XX:MaxDirectMemorySize是java运行参数,用户控制java程序可以使用的最大直接内存(堆外/本地);
-Dio.netty.maxDirectMemory是netty运行参数,用户控制netty程序可以使用的最大直接内存(堆外/本地);
直接使用JDK代码时:
只有调用了这个java.nio.DirectByteBuffer#DirectByteBuffer(int)构造器方法进行直接内存申请的时候,才会受-XX:MaxDirectMemorySize参数限制。
直接调用Unsafe类,-XX:MaxDirectMemorySize 参数的大小限制对这种是无效的。
原因:
java.nio.DirectByteBuffer#DirectByteBuffer(int)构造器方法受到 -XX:MaxDirectMemorySize 参数的限制,是因为在方法java.nio.DirectByteBuffer方法中在申请内存前需要先
调用Bits.reserveMemory(size, cap)进行预留,如果无法预留,就会抛出OOM;
在Bits.reserveMemory(size, cap)内部不断通过java.nio.Bits#tryReserveMemory尝试
看看有没有足够的堆外内存:cap <= MAX_MEMORY - (totalCap = TOTAL_CAPACITY.get())
这里的 MAX_MEMORY就是-XX:MaxDirectMemorySize 参数值。
// A user-settable upper limit on the maximum amount of allocatable
// direct buffer memory. This value may be changed during VM
// initialization if it is launched with "-XX:MaxDirectMemorySize=<size>".
private static volatile long MAX_MEMORY = VM.maxDirectMemory();
// -XX:MaxDirectMemorySize not given, take default
directMemory = Runtime.getRuntime().maxMemory();
// Runtime.getRuntime().maxMemory(); Returns the maximum amount of memory that the Java virtual machine will attempt to use. If there is no inherent limit then the value Long.MAX_VALUE will be returned.
使用Netty代码时:
在Netty中,一般都是通过io.netty.buffer.AbstractByteBufAllocator#directBuffer(int, int)方法申请直接内存的,
该方法内部是调用了io.netty.buffer.AbstractByteBufAllocator#newDirectBuffer, 该方法在不同的实现类中,有不同的实现。
当实现类为:io.netty.buffer.PooledByteBufAllocator:
在io.netty.buffer.PooledByteBufAllocator#newDirectBuffer中:
当PlatformDependent.hasUnsafe()为true的时候:
1.当要使用Cleaner的时候,最终调用的是JDK中,只在 java.nio.ByteBuffer#allocateDirect这个工具方法中使用,所以受-XX:MaxDirectMemorySize参数限制;
2.当PlatformDependent.useDirectBufferNoCleaner()(具体逻辑还要看),调用io.netty.util.internal.PlatformDependent0#allocateDirectNoCleaner申请直接内存,此时不受-XX:MaxDirectMemorySize参数限制;受netty的参数限制。
当PlatformDependent.hasUnsafe()为false的时候:
最终调用的是JDK中,只在 java.nio.ByteBuffer#allocateDirect这个工具方法中使用,所以受-XX:MaxDirectMemorySize参数限制;
当实现类为:io.netty.buffer.UnpooledByteBufAllocator:
在io.netty.buffer.UnpooledByteBufAllocator#newDirectBuffer中:
当PlatformDependent.hasUnsafe()为true的时候:
1.当要使用Cleaner的时候,最终调用的是JDK中,只在 java.nio.ByteBuffer#allocateDirect这个工具方法中使用,所以受-XX:MaxDirectMemorySize参数限制;
2.当noCleaner(具体逻辑还要看),调用io.netty.util.internal.PlatformDependent0#allocateDirectNoCleaner申请直接内存,此时不受-XX:MaxDirectMemorySize参数限制;受netty的参数限制。
当PlatformDependent.hasUnsafe()为false的时候:
最终调用的是JDK中,只在 java.nio.ByteBuffer#allocateDirect这个工具方法中使用,所以受-XX:MaxDirectMemorySize参数限制;
在Netty 4.0版本中,默认的分配器为UnpooledByteBufAllocator。而在Netty 4.1版本中,默认的分配器为PooledByteBufAllocator;
在netty4.1 以后默认noCleaner策略。
总结:
-XX:MaxDirectMemorySize
默认值是JVM能从操作系统申请的最大内存,如果内存本身没限制,则值为Long.MAX_VALUE个字节。
使用JDK时:
调用java.nio.DirectByteBuffer#DirectByteBuffer(int)构造器方法进行直接内存申请的时候,才会受-XX:MaxDirectMemorySize参数限制。
直接调用Unsafe类,-XX:MaxDirectMemorySize 参数的大小限制对这种是无效的。
使用Netty时:
Netty4.1以后,-Dio.netty.maxDirectMemory有用(默认noCleaner策略,-XX:MaxDirectMemorySize不起作用);
Netty4.1以前,-Dio.netty.maxDirectMemory和-Dio.netty.maxDirectMemory都有用
需要进行直接内存限制时,最好两个参数都加上以防万一
参考:
【Netty学习】七、详解ByteBuf缓冲区_51CTO博客_netty writeandflush并发
Netty11# 非池化内存分配-腾讯云开发者社区-腾讯云 (tencent.com)