在没有任何jvm参数配置的情况下,默认System.gc会触发一次full gc。针对cms gc的情况下,默认其实执行的是serial gc。
通过配置-XX:+ExplicitGCInvokesConcurrent 来避免执行serial gc。
参考源代码:
voidGenCollectedHeap::collect(GCCause::Causecause){
if(should_do_concurrent_full_gc(cause)){
#ifndefSERIALGC// mostly concurrent full
collectioncollect_mostly_concurrent(cause);
#else// SERIALGC
ShouldNotReachHere();
#endif// SERIALGC
}else{
#ifdefASSERT
if(cause==GCCause::_scavenge_alot){
// minor collection only
collect(cause,0);
}else{
// Stop-the-world full
collectioncollect(cause,n_gens()-1);
}
#else// Stop-the-world full
collectioncollect(cause,n_gens()-1);
#endif
}
}
boolGenCollectedHeap::should_do_concurrent_full_gc(GCCause::Causecause){
returnUseConcMarkSweepGC&&((cause==GCCause::_gc_locker&&GCLockerInvokesConcurrent)||(cause==GCCause::_java_lang_system_gc&&ExplicitGCInvokesConcurrent));
}
使用场景:
在使用堆外内存的时候,需要使用到System.gc。
1.在申请对外内存的时候,如果发现可以分配的堆外内存不够的时候,试图通过gc去释放堆外内存。堆外内存的大小限制和jvm参数-XX:MaxDirectMemorySize有关
2.使用完堆外内存时,可以使用System.gc来尝试释放。也可以获取DirectByteBuffer对象关联的Cleaner来直接释放。
gc和堆外内存的关系:
首先在java层面和在堆外分配的这块内存关联的只有与之关联的DirectByteBuffer对象了,它记录了这块内存的地址以及大小。
gc如何能通过操作DirectByteBuffer对象来间接操作对应的堆外内存?
DirectByteBuffer对象在创建的时候关联了一个PhantomReference,说到PhantomReference它其实主要是用来跟踪对象何时被回收的,它不能影响gc决策,但是gc过程中如果发现某个对象除了只有PhantomReference引用它之外,并没有其他的地方引用它了,那将会把这个引用放到java.lang.ref.Reference.pending队列里,在gc完毕的时候通知ReferenceHandler这个守护线程去执行一些后置处理,而DirectByteBuffer关联的PhantomReference是PhantomReference的一个子类,在最终的处理里会通过Unsafe的free接口来释放DirectByteBuffer对应的堆外内存块