Java_JVM|GC问题记录 – DirectByteBuffer

背景 【Java_JVM|GC问题记录 – DirectByteBuffer】项目因使用cxf框架进行webservice调用,gc日志会出现周期性显示System.gc()的full gc调用,每十小时执行一次。
采取的解决手段:增加JVM参数-XX:+DisableExplicitGC,这个参数会使显示的调用System.gc()空转,不会执行垃圾回收。
但是,引起了另外一个问题:系统一直正常运行着,没有进行full gc,实际java的堆内存占比正常,然而系统的物理应用内存占用量很大,影响到了系统的运行。
原因 ByteBuffer有两种:
heap ByteBuffer -> -XX:Xmx
1.一种是heap ByteBuffer,该类对象分配在JVM的堆内存里面,直接由Java虚拟机负责垃圾回收,
direct ByteBuffer -> -XX:MaxDirectMemorySize
2.一种是direct ByteBuffer是通过jni在虚拟机外内存中分配的。通过jmap无法查看该快内存的使用情况。只能通过top来看它的内存使用情况。
而directByteBuffer,其申请的native memory只有在old gen GC(full GC/major GC或者concurrent GC)时才可以回收。主要原理是Full GC的会对old gen做reference processing,进而可以触发Cleaner对已死的DirectByteBuffer对象做清理工作;而如果很长一段时间没做过GC或者只做了young GC,则不会在old gen触发Cleaner的工作,那么就可能让本来已经死了的、但已经晋升到old gen的DirectByteBuffer关联的native memory得不到及时释放。
调优方法相应的有两种
1.调整-Xmx的大小;我们可以把应用的最大内存设置小一些,这样尽可能快地使native memory达到MaxDirectMemorySize的上限,进行回收。这样的话,其实应用的内存使用受到了限制,除非应用本身对于内存的使用不敏感,这样才是可行的;否则反而会浪费内存。

2. 显式设置MaxDirectMemorySize。
此参数的含义是当Direct ByteBuffer分配的堆外内存到达指定大小后,即触发Full GC。注意该值是有上限的,默认是64M,单位可以使用k/K、m/M、g/G,最大为sun.misc.VM.maxDirectMemory()。
我们可以直接把native memory设置小一些;让native memory的回收尽可能地快。

    推荐阅读