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

// 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)

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

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


 

    

{"_nodes":{"total":1,"successful":1,"failed":0},"cluster_name":"docker-cluster","nodes":{"eDsFPsbOR6GnXPleFbftRQ":{"name":"83c7d586deb2","transport_address":"172.17.0.2:9300","host":"172.17.0.2","ip":"172.17.0.2","version":"7.4.0","build_flavor":"default","build_type":"docker","build_hash":"22e1767283e61a198cb4db791ea66e3f11ab9910","roles":["ingest","master","data","ml"],"attributes":{"ml.machine_memory":"33566298112","xpack.installed":"true","ml.max_open_jobs":"20"},"jvm":{"pid":1,"version":"13","vm_name":"OpenJDK 64-Bit Server VM","vm_version":"13+33","vm_vendor":"AdoptOpenJDK","bundled_jdk":true,"using_bundled_jdk":true,"start_time_in_millis":1717186543275,"mem":{"heap_init_in_bytes":1073741824,"heap_max_in_bytes":1037959168,"non_heap_init_in_bytes":7667712,"non_heap_max_in_bytes":0,"direct_max_in_bytes":0},"gc_collectors":["ParNew","ConcurrentMarkSweep"],"memory_pools":["CodeHeap 'non-nmethods'","Metaspace","CodeHeap 'profiled nmethods'","Compressed Class Space","Par Eden Space","Par Survivor Space","CodeHeap 'non-profiled nmethods'","CMS Old Gen"],"using_compressed_ordinary_object_pointers":"true","input_arguments":["-Xms1g","-Xmx1g","-XX:+UseConcMarkSweepGC","-XX:CMSInitiatingOccupancyFraction=75","-XX:+UseCMSInitiatingOccupancyOnly","-Des.networkaddress.cache.ttl=60","-Des.networkaddress.cache.negative.ttl=10","-XX:+AlwaysPreTouch","-Xss1m","-Djava.awt.headless=true","-Dfile.encoding=UTF-8","-Djna.nosys=true","-XX:-OmitStackTraceInFastThrow","-Dio.netty.noUnsafe=true","-Dio.netty.noKeySetOptimization=true","-Dio.netty.recycler.maxCapacityPerThread=0","-Dio.netty.allocator.numDirectArenas=0","-Dlog4j.shutdownHookEnabled=false","-Dlog4j2.disable.jmx=true","-Djava.io.tmpdir=/tmp/elasticsearch-7454396193756508186","-XX:+HeapDumpOnOutOfMemoryError","-XX:HeapDumpPath=data","-XX:ErrorFile=logs/hs_err_pid%p.log","-Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m","-Djava.locale.providers=COMPAT","-Des.cgroups.hierarchy.override=/","-Dio.netty.allocator.type=unpooled","-XX:MaxDirectMemorySize=536870912","-Des.path.home=/usr/share/elasticsearch","-Des.path.conf=/usr/share/elasticsearch/config","-Des.distribution.flavor=default","-Des.distribution.type=docker","-Des.bundled_jdk=true"]}}}}
08-21
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值