jvm-堆外内存GC问题 jvm-堆外内存GC问题
jvmGC的特例
java编程的时候有没有情况需要手动调用system.gc()?
在使用了native的场景下
- 使用了NIO或者NIO框架(Mina/Netty)
- 使用了DirectByteBuffer分配字节缓冲区
- 使用了MappedByteBuffer做内存映射
由于Native Memory只能通过FullGC(或是CMS GC)回收,所以除非你非常清楚这时真的有必要,否则不要轻易调用System.gc(),且行且珍惜。 另外为了防止某些框架中的System.gc调用(例如NIO框架、Java RMI),建议在启动参数中加上-XX:+DisableExplicitGC来禁用显式GC。 这个参数有个巨大的坑,如果你禁用了System.gc(),那么上面的3种场景下的内存就无法回收,可能造成OOM,如果你使用了CMS GC,那么可以用这个参数替代:-XX:+ExplicitGCInvokesConcurrent。
CMS GC会不会回收Direct ByteBuffer的内存?
Oracle JDK 6u32前的版本不会. Direct ByteBuffer是在java Head外分配内存,但是Direct ByteBuffer分配出去的内存也是由GC管理的。HotSpot在GC时会扫描Direct ByteBuffer 对象是否有引用,如果没有则同时也会回收其占用的对外内存。但不幸的是在6u32前的版本里,CMS GC有bug会导致可能回收不掉,具体的bug id为7112034,在链接的Backport信息里,可以看到这个bug是在hotspot 20.7的版本里修复的
Direct Buffer gc的另一中情况
如果Direct Buffer对象晋升到了Old区,那么这个时候就只能等Full GC触发,因此在Direct ByteBuffer 使用较多,存活时间较长的情况下,有可能会导致 堆外内存耗光 对于上面这种类型的应用,最好是在启动参数上增加-XX:MaxDirectMemorySize=x[m|g],例如-XX:MaxDirectMemorySize=500m 这个参数默认的大小是-Xmx的值,此参数的含义是当Direct ByteBuffer分配的堆外内存达到指定大小后,即触发Full GC,如Full GC后仍然分配不出Direct ByteBuffer需要的空间,则会报OOM错误:
因为上面所说的状况,如碰到堆外内存占用较多的场景,可以尝试强制执行Full GC(强制的方法为执行jmap -histo:live) 看看,多执行一两次,如堆外内存下降的话,很有可能就是Direct ByteBuffer造成的,对于这种情况,通常加上上面的启动参数就可解决。