-XX:MaxDirectMemorySize和-Dio.netty.maxDirectMemory区别

-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 参数值。

java 复制代码
// 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环境为例:

bash 复制代码
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)

一文搞懂堆外内存(模拟内存泄漏)-CSDN博客

https://www.cnblogs.com/xiaojiesir/p/15449937.html

相关推荐
青花锁1 年前
【JVM面试】从JDK7 到 JDK8, JVM为啥用元空间替换永久代?
jvm·面试·职场和发展·运行时数据区·老年代·元空间·直接内存