-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.
堆外内存大小:
可以看到,当我们设置了-XX:MaxDirectMemorySize 参数,java.nio.DirectByteBuffer#DirectByteBuffer(int)可以使用的堆外内存最大值就是它;
当我们没有设置-XX:MaxDirectMemorySize 参数,只设置了-Xmx,那么java.nio.DirectByteBuffer#DirectByteBuffer(int)可以使用的堆外内存空间大小就是堆的最大的 可使用的大小。
当我们没有设置-XX:MaxDirectMemorySize 参数,也没有设置了-Xmx ,那么此时java.nio.DirectByteBuffer#DirectByteBuffer(int)可以使用的堆外内存空间大小是Xmx的默认值
而Xmx的默认值一般是物理内存的1/4,但这可能会根据不同的JVM版本和操作系统有所变化。所以最好在生产环境中明确指定这些值。
以我自己的Windows环境为例:
java -XX:+PrintFlagsFinal -version | findstr /i "HeapSize"
size_t ErgoHeapSizeLimit = 0 {product} {default}
size_t HeapSizePerGCThread = 43620760 {product} {default}
size_t InitialHeapSize = 536870912 {product} {ergonomic}
size_t LargePageHeapSizeThreshold = 134217728 {product} {default}
size_t MaxHeapSize = 8539602944 {product} {ergonomic}
size_t MinHeapSize = 8388608 {product} {ergonomic}
uintx NonNMethodCodeHeapSize = 7602480 {pd product} {ergonomic}
uintx NonProfiledCodeHeapSize = 122027880 {pd product} {ergonomic}
uintx ProfiledCodeHeapSize = 122027880 {pd product} {ergonomic}
size_t SoftMaxHeapSize = 8539602944 {manageable} {ergonomic}
java version "17.0.8" 2023-07-18 LTS
Java(TM) SE Runtime Environment (build 17.0.8+9-LTS-211)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.8+9-LTS-211, mixed mode, sharing)
Xmx的默认值为size_t MaxHeapSize = 8539602944 byte
为8144M,可以通过打印long directMemory = Runtime.getRuntime().maxMemory();来验证
在Linux中查看Java堆大小,Xmx、Xms的默认值和当前运行时的设置值 - 哔哩哔哩
使用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的最大堆内存大小,默认为物理内存的1/4.
使用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都有用
需要进行直接内存限制时,最好两个参数都加上以防万一
参考:
直接内存(堆外内存)-CSDN博客
【Netty学习】七、详解ByteBuf缓冲区_51CTO博客_netty writeandflush并发
Netty11# 非池化内存分配-腾讯云开发者社区-腾讯云 (tencent.com)