[讨论] 关于NIO的ByteBuffer分配的本地内存只有fullGC才能回收到的问题
ningandjin
2012-01-14
各位新年快乐啊;
这是别人定位过的一个问题,问题虽然解决了,但是解释我还不能完全信服,所以到这儿来请教请教各位大牛 . 问题是这样的啊,JVM设置了15G的内存,JVM配置的目的是不发生FullGC,但是系统运行2天的时候,发现服务器的内存被占用了30G,但是JConsole,Jmap去看JVM的heap,没有任何异常,通过查看服务内存信息,发现有一个13G的堆外空间被占用,里面存储的信息都是NIO客户端曾经发送过的信息。调查发现,NIO会使用ByteBuffer .allocate()分配堆外内存,用作发送消息的缓冲区使用,然后因为一直没有发生FullGC,这部分内存就一直没有得到释放,就导致了这个内存占用到30G的情况,当最后发生FullGC的时候,所有的内存都回收到了。所以他们解决问题的方法就是配置了一个定时fullGC参数。每隔一个小时或者几个小时fullGC 一次,但是我不明白的是,为什么MinorGC没有把这个堆外内存回收到啊?JVM对堆外内存的管理方式是怎样的啊?希望大大能够推荐点资料,加深一下理解,谢谢。 |
|
RednaxelaFX
2012-01-15
请阅读置顶帖,http://hllvm.group.iteye.com/group/topic/27945
第一个案例。 |
|
ningandjin
2012-01-15
|
|
ningandjin
2012-01-16
RednaxelaFX 写道 请阅读置顶帖,http://hllvm.group.iteye.com/group/topic/27945
第一个案例。 JDK version: java version "1.6.0_21" Java(TM) SE Runtime Environment (build 1.6.0_21-b07) Java HotSpot(TM) Client VM (build 17.0-b17, mixed mode, sharing) OS: windows 7,内存2G R大,再问个问题啊, System.out.println(sun.misc.VM.maxDirectMemory()); Class c = Class.forName("java.nio.Bits"); Field maxMemory = c.getDeclaredField("maxMemory"); maxMemory.setAccessible(true); synchronized (c) { Long maxMemoryValue = (Long)maxMemory.get(null); System.out.println("maxMemoryValue:"+maxMemoryValue); } 执行结果如下: maxMemoryValue:259522560 maxMemoryValue:67108864 看java.nio.Bits代码 // 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 maxMemory = VM.maxDirectMemory(); 一样的逻辑处理,为什么2者会差别这么大啊? |
|
RednaxelaFX
2012-01-16
嗯?你的代码跟你的输出结果看起来不对应。
顺带请看看置顶帖里的第5点。 |
|
ningandjin
2012-01-16
RednaxelaFX 写道 嗯?你的代码跟你的输出结果看起来不对应。
顺带请看看置顶帖里的第5点。 sorry,为了结果看起来对称,改动了第一行代码: System.out.println("maxMemoryValue:"+sun.misc.VM.maxDirectMemory()); 第5点我也看过了,而且测试过了,的确是按R大说的分配的,于是我想测一下不配置任何参数下的效果,结果看到有人说默认是64M,于是我把2个方法放在一起做了比较,发现2个结果不一样,但是从源代码来看,2者都是调用的VM.maxDirectMemory(),很费解,所以上来求教于R大。 |
|
RednaxelaFX
2012-01-16
那是因为你用反射去获取java.nio.Bits.maxMemory的值的时候,这个字段还没最终初始化。最终用的值是这样初始化的:
if (!memoryLimitSet && VM.isBooted()) { maxMemory = VM.maxDirectMemory(); memoryLimitSet = true; } 在Bits.reserveMemory()里。读代码别只看一半。 你试试在反射获取这个值之前先随便调个ByteBuffer.allocateDirect(...) D:\experiment>\sdk\groovy-1.7.2\bin\groovysh Groovy Shell (1.7.2, JVM: 1.6.0_26) Type 'help' or '\h' for help. ----------------------------------------------------------------- groovy:000> sun.misc.VM.maxDirectMemory() ===> 1037959168 groovy:000> java.nio.Bits.maxMemory ===> 67108864 groovy:000> java.nio.ByteBuffer.allocateDirect(2) ===> java.nio.DirectByteBuffer[pos=0 lim=2 cap=2] groovy:000> java.nio.Bits.maxMemory ===> 1037959168 groovy:000> quit |
|
ningandjin
2012-01-16
R大,从代码上来看
private static volatile long maxMemory = VM.maxDirectMemory();, 我们在反射Bits类的时候,应该会加载maxMemory这个static的属性吧?如果加载,则此时会调用VM.maxDirectMemory()方法,此时调用的VM.maxDirectMemory()和我直接调用sun.misc.VM.maxDirectMemory()的差别在哪儿? 是因为VM这个类在我反射的时候还没有初始化完吗? 另外,求R大发一个能sun.misc.*包中这些的源码连接,我到网上找了好久,都没找到源码包下。十分感谢。 |
|
RednaxelaFX
2012-01-16
代码在OpenJDK里就有。
那个静态初始化会被后面第一次调用Bits.reserveMemory()的初始化刷新掉。这边需要详细解释么? |
|
ningandjin
2012-01-17
RednaxelaFX 写道 代码在OpenJDK里就有。
那个静态初始化会被后面第一次调用Bits.reserveMemory()的初始化刷新掉。这边需要详细解释么? R大,早上好,敬老大一杯茶 ,谢谢老大的指导。这儿我想了解一下,一个Java main函数启动的时候,在执行到我们自己写的代码之前的做了些什么。比方说这个VM类的初始化。。。。 在我的代码中: System.out.println("maxMemoryValue:"+sun.misc.VM.maxDirectMemory()); Class c = Class.forName("java.nio.Bits"); Field maxMemory = c.getDeclaredField("maxMemory"); maxMemory.setAccessible(true); synchronized (c) { Long maxMemoryValue = (Long)maxMemory.get(null); System.out.println("maxMemoryValue:"+maxMemoryValue); } 2者皆没有调用到Bits.reserveMemory()这个方法啊,是不是因为Java的反射机制啊? |