先上test代码
public class DirectMemoryTest {
public static void main(String[] args) {
ByteBuffer buffer =null;
try {
long startTime=System.currentTimeMillis();
for(int i=0;i<100;i++){
buffer = ByteBuffer.allocateDirect(1024 *1024 *250);
//clean(buffer);
}
long endTime=System.currentTimeMillis();
System.out.println("程序运行时间: "+(endTime-startTime)+"ms");
//System.out.println(null+"");
}catch (Exception e) {
e.printStackTrace();
}
}
public static void clean(final ByteBuffer byteBuffer) {
if (byteBuffer.isDirect()) {
((DirectBuffer)byteBuffer).cleaner().clean();
}
}
}
运行使用的jvm参数 打印gc日志,直接内存设置最大1G
-XX:+PrintGCDetails -XX:+UseConcMarkSweepGC -XX:MaxDirectMemorySize=1024M
不调用clean()方法的时候,会频繁的发生由于system.gc()造成的full gc,此时老年代并未使用很多
[Full GC (System.gc()) [CMS: 0K->657K(87424K), 0.0104483 secs] 2801K->657K(126720K), [Metaspace: 3390K->3390K(1056768K)], 0.0105395 secs] [Times: user=0.00 sys=0.02, real=0.01 secs]
[Full GC (System.gc()) [CMS: 657K->652K(87424K), 0.0054286 secs] 658K->652K(126848K), [Metaspace: 3391K->3391K(1056768K)], 0.0055050 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC (System.gc()) [CMS: 652K->652K(87424K), 0.0048721 secs] 1354K->652K(126848K), [Metaspace: 3391K->3391K(1056768K)], 0.0049353 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC (System.gc()) [CMS: 652K->652K(87424K), 0.0055415 secs] 652K->652K(126848K), [Metaspace: 3391K->3391K(1056768K)], 0.0056139 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
当调用了clean()方法的时候,却不会发生任何一次fullGC
这是为什么呢?继续上代码,DirectByteBuffer初始化的时候,内部会显示调用System.gc();
image.png
image.png
关键就在这里,显示调用System.gc(),当jvm发现直接内存MaxDirectMemorySize=1024M所剩无几的时候,就会触发fullGC。
现在大概是1G直接内存,每次申请250M,可以发现3个循环之后会触发一次fullgc.如果每次申请的大小继续增大,比如400
m,可以发现2个循环之后就会fullgc。
这也就解释了,为什么当调用了clean()方法的时候,却不会发生任何一次fullGC,因为每一次都会手动的释放内存,jvm即使接到
System.gc()命令,但是认为没必要进行gc,就不会触发。
解决:1.手动释放 ((DirectBuffer)byteBuffer).cleaner().clean()
2.禁用System.gc(),参数 -XX:-+DisableExplicitGC 有可能会导致,直接内存得不到回收,发生OOM(此时,只有发生堆内存回收的时候,才会顺带的回收直接内存)
网友评论