前言 

最近在 记一次 netty 内存泄露 中有还有一些问题 

比如 java.nio.ByteBuffer 中分配空间的时候, 直接内存 受限于 Bits.maxMemory 

在 netty 中的 PlatformDependent 中分配空间的时候, 直接内存 受限于 PlatformDependent.DIRECT_MEMORY_LIMIT 

然后 这两个 maxMemory 均是来自于 VM.maxDirectMemory, 那么 这个 VM.maxDirectMemory 又是怎么来的呢? 

这个问题之所以要探究一下, 是因为我之前配置 MaxDirectMemorySize 似乎是不生效 还是怎么的?, 但是 今天试了一下 又可以了 

以下代码截图基于 jdk8 

调试不到的代码?

来看一下 VM.maxDirectMemory, 取自于 属性 directMemory 

47 VM.maxDirectMemory() 来自于哪里_System

然后 我们看一下 这个字段初始化的地方 

1. 它是有一个默认值 

47 VM.maxDirectMemory() 来自于哪里_初始化_02

2. 是 VM.saveAndRemoveProperties 里面的初始化代码 

47 VM.maxDirectMemory() 来自于哪里_初始化_03

然后 我们打上断点查看, 发现 初始化部分的代码 这里居然断点停不了? 这是一个很神奇的事情 

但是 获取 maxMemorty 的时候, maxMemory 已经是一个正常的我们期望的数值了 

但是 它是在哪里初始化的? 难道还有其他地方 ? 

47 VM.maxDirectMemory() 来自于哪里_初始化_04

基于 HotspotVM 的调试  

呵呵 java 层面看不到什么情况, 只能来 vm 调试一下了 

可以看到 System.initPhase1 里面某一个步骤调用了 Runtime.maxMemory 

System.initPhase1 是 jdk9 中的初始化方法, 对应于 jdk8 是 System.initializeSystemClass 

47 VM.maxDirectMemory() 来自于哪里_maxmemorty_05

输出一下当前线程的堆栈信息 

at java.lang.Runtime.maxMemory(java.base/Native Method)
	at jdk.internal.misc.VM.saveAndRemoveProperties(java.base/VM.java:190)
	at java.lang.System.initPhase1(java.base/System.java:1949)

jdk 的 VM 里面 190 行对应于初始化 directMemory, 这个就证明了 上面我们不确定的东西 

VM.maxMemorty 的初始化确实是在 VM.saveAndRemoveProperties 里面初始化的 

47 VM.maxDirectMemory() 来自于哪里_System_06

另外我们看一下 默认值, 取自 Runtime.maxMemorty 

Runtime.maxMemorty 的实现是取自 堆空间的大小 

47 VM.maxDirectMemory() 来自于哪里_maxmemorty_07

为什么 VM.saveAndRemoveProperties 调试不到? 

调试的文章, 可以参考 调试启动 suspend=y 的情况下, jps 得到 -- main class information unavailable

我们看一下 jvmti 暴露 jdwp 服务初始化的地方, 这里 bagEnumrateOver 是 bind 端口, 接受 jdwp 服务的地方 

仔细看 这里是在 Threads::create_vm 3800 行的地方, 而上面调用 System.initPhase1/initializeSystemClass 是在 Threads::create_vm 3700 行的地方 

此时 jdwp 相关服务尚未暴露, 因此是 客户端是感知不到的 

47 VM.maxDirectMemory() 来自于哪里_初始化_08

如何配置 VM.maxDirectMemory ?

从上面 VM 的代码里面我们可以看到 读取的配置是 sun.nio.MaxDirectMemorySize 

那么可以直接配置 sun.nio.MaxDirectMemorySize 嘛? 答案是否定的  

VM.saveAndRemoveProperties 传入的 properties 来自于这里 

1. Arguments::system_properties 里面是没有 "sun.nio.MaxDirectMemorySize" 属性的

2. 另外 下面有一段关于 sun.nio.MaxDirectMemorySize 和 MaxDirectMemorySize 的特殊处理, 检查是否有 MaxDirectMemorySize 参数, 如果没有, 设置 sun.nio.MaxDirectMemorySize 为 -1, 否则 设置 MaxDirectMemorySize 的值到 sun.nio.MaxDirectMemorySize

47 VM.maxDirectMemory() 来自于哪里_maxmemorty_09

本地内存使用可以超过 VM.maxMemorty 配置的吗?

这个答案是 肯定的 

不管是 java.nio.DirectBuffer 分配空间, 还是 PlatformDependent 分配空间 

对于 maxMemorty 的限制都是逻辑上的限制 

如果 我设置 MaxDirectMemorySize 为 32M, java.nio.DirectBuffer 分配 29M, PlatformDependent 分配 29M, 也是可以的 

呵呵 算是 tricks 吧 

47 VM.maxDirectMemory() 来自于哪里_maxmemorty_10

 参考 

  记一次 netty 内存泄露   调试启动 suspend=y 的情况下, jps 得到 -- main class information unavailable