读书笔记之inside JVM(5)

java提供了从语言角度能够强制jvm进行垃圾回收,在我们的程序中可以通过调用System.gc去强制jvm进行垃圾回收,通过源码我们可以看到实际上是调用了Runtime去强制gc

[java]   view plain copy
  1. public static void gc() {  
  2.     Runtime.getRuntime().gc();  
  3.     }  

Runtime的gc方法是native方法也就是Rumtime.c中的

[cpp]   view plain copy
  1. JNIEXPORT void JNICALL  
  2. Java_java_lang_Runtime_gc(JNIEnv *env, jobject this)  
  3. {  
  4.     JVM_GC();  
  5. }  

而JVM_GC方法是在jvm.cpp中实现

[cpp]   view plain copy
  1. JVM_ENTRY_NO_ENV(void, JVM_GC(void))  
  2.   JVMWrapper("JVM_GC");  
  3.   if (!DisableExplicitGC) {  
  4.     Universe::heap()->collect(GCCause::_java_lang_system_gc);  
  5.   }  
  6. JVM_END  

我们看到参数DisableExplicitGC,从代码中可以使用-XX:+DisableExplicitGC 可以关闭system,gc

对universe:heap() -> Universe::_collectedHeap 是在初始化的时候决定使用什么类型的heap,也决定了使用什么gc的策略,也就是说java heap的分配方式是在java启动的时候就决定的,无法中间更改,同样对应的gc策略也无法更改

[cpp]   view plain copy
  1. if (UseParallelGC) {  
  2. #ifndef SERIALGC  
  3.     Universe::_collectedHeap = new ParallelScavengeHeap();  
  4. #else  // SERIALGC  
  5.     fatal("UseParallelGC not supported in java kernel vm.");  
  6. #endif // SERIALGC  
  7.   
  8.   } else if (UseG1GC) {  
  9. #ifndef SERIALGC  
  10.     G1CollectorPolicy* g1p = new G1CollectorPolicy_BestRegionsFirst();  
  11.     G1CollectedHeap* g1h = new G1CollectedHeap(g1p);  
  12.     Universe::_collectedHeap = g1h;  
  13. #else  // SERIALGC  
  14.     fatal("UseG1GC not supported in java kernel vm.");  
  15. #endif // SERIALGC  
  16.   
  17.   } else {  
  18.     GenCollectorPolicy *gc_policy;  
  19.   
  20.     if (UseSerialGC) {  
  21.       gc_policy = new MarkSweepPolicy();  
  22.     } else if (UseConcMarkSweepGC) {  
  23. #ifndef SERIALGC  
  24.       if (UseAdaptiveSizePolicy) {  
  25.         gc_policy = new ASConcurrentMarkSweepPolicy();  
  26.       } else {  
  27.         gc_policy = new ConcurrentMarkSweepPolicy();  
  28.       }  
  29. #else   // SERIALGC  
  30.     fatal("UseConcMarkSweepGC not supported in java kernel vm.");  
  31. #endif // SERIALGC  
  32.     } else { // default old generation  
  33.       gc_policy = new MarkSweepPolicy();  
  34.     }  
  35.   
  36.     Universe::_collectedHeap = new GenCollectedHeap(gc_policy);  
  37.   }  
从源码中我们可以看到

Parallel GC 是用的是ParallelScavengeHeap, 

CMS 使用的是 GenCollectdHeap 

G1使用的是G1CollectedHeap

java的gc回收的类型主要有几种 UseSerialGC,UseConcMarkSweepGC,UseParNewGC,UseParallelGC,UseParallelOldGC,UseG1GC,而这几个参数是如何搭配的,实际上只要看下面的代码就非常清楚

[cpp]   view plain copy
  1. bool Arguments::check_gc_consistency() {  
  2.   bool status = true;  
  3.   // Ensure that the user has not selected conflicting sets  
  4.   // of collectors. [Note: this check is merely a user convenience;  
  5.   // collectors over-ride each other so that only a non-conflicting  
  6.   // set is selected; however what the user gets is not what they  
  7.   // may have expected from the combination they asked for. It's  
  8.   // better to reduce user confusion by not allowing them to  
  9.   // select conflicting combinations.  
  10.   uint i = 0;  
  11.   if (UseSerialGC)                       i++;  
  12.   if (UseConcMarkSweepGC || UseParNewGC) i++;  
  13.   if (UseParallelGC || UseParallelOldGC) i++;  
  14.   if (UseG1GC)                           i++;  
  15.   if (i > 1) {  
  16.     jio_fprintf(defaultStream::error_stream(),  
  17.                 "Conflicting collector combinations in option list; "  
  18.                 "please refer to the release notes for the combinations "  
  19.                 "allowed\n");  
  20.     status = false;  
  21.   }  
  22.   
  23.   return status;  
  24. }  

我们把GC分成4种类型

1.  SerialGC 

参数-XX:+UseSerialGC

就是Young区和old区都使用serial 垃圾回收算法,

2.  ParallelGC 

参数-XX:+UseParallelGC

Young区:使用Parallel scavenge 回收算法

Old  区:可以使用单线程的或者Parallel 垃圾回收算法,由 -XX:+UseParallelOldGC 来控制

3.  CMS  

参数-XX:+UseConcMarkSweepGC

Young区:可以使用普通的或者parallel 垃圾回收算法,由参数 -XX:+UseParNewGC来控制

Old 区:只能使用Concurrent Mark Sweep 

4. G1 

参数:-XX:+UseG1GC

没有young/old区

在第一篇中曾经提到过,System.gc 曾经调用代码

[cpp]   view plain copy
  1. Universe::heap()->collect(GCCause::_java_lang_system_gc);  

而每个不同类型的gc,使用不同的heap策略,以parallelScavengeHeap 为例子
[cpp]   view plain copy
  1. void ParallelScavengeHeap::collect(GCCause::Cause cause) {  
  2.   assert(!Heap_lock->owned_by_self(),  
  3.     "this thread should not own the Heap_lock");  
  4.   
  5.   unsigned int gc_count      = 0;  
  6.   unsigned int full_gc_count = 0;  
  7.   {  
  8.     MutexLocker ml(Heap_lock);  
  9.     // This value is guarded by the Heap_lock  
  10.     gc_count      = Universe::heap()->total_collections();  
  11.     full_gc_count = Universe::heap()->total_full_collections();  
  12.   }  
  13.   
  14.   VM_ParallelGCSystemGC op(gc_count, full_gc_count, cause);  
  15.   VMThread::execute(&op);  
  16. }  

代码中显示当前线程提交了一个operation VM_ParallelGCSystemGC 到了VMThread 线程,关于VMThread线程请参考以前写的2篇文章

http://blog.csdn.net/raintungli/article/details/7045024  VMThread

http://blog.csdn.net/raintungli/article/details/6553337  VMThread  执行的operation

而对VM_ParallelGCSystemGC, 最后VMThread 调用了doit的方法

[cpp]   view plain copy
  1. void VM_ParallelGCSystemGC::doit() {  
  2.   JvmtiGCFullMarker jgcm;  
  3.   notify_gc_begin(true);  
  4.   
  5.   ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap();  
  6.   assert(heap->kind() == CollectedHeap::ParallelScavengeHeap,  
  7.     "must be a ParallelScavengeHeap");  
  8.   
  9.   GCCauseSetter gccs(heap, _gc_cause);  
  10.   if (_gc_cause == GCCause::_gc_locker  
  11.       DEBUG_ONLY(|| _gc_cause == GCCause::_scavenge_alot)) {  
  12.     // If (and only if) the scavenge fails, this will invoke a full gc.  
  13.     heap->invoke_scavenge();  
  14.   } else {  
  15.     heap->invoke_full_gc(false);  
  16.   }  
  17.   notify_gc_end();  
  18. }  

也就是调用了parallelScavengeHeap 的invoke_full_gc方法。

当前线程提交了一个GC的operation 给了VMThread线程, 由VMThread线程来执行回收,因为VMThread线程是执行在Queue里的任务,也就是system.gc 未必会及时触发因为queue里可能还是有其他的任务在执行,同样VMThread在执行GC的时候,queue里的其他任务也会因此而无法执行比如thread.stop

 

为了提高垃圾回收的性能,java在parallel回收的时候可以设置同时并行处理的线程数也就是ParallelGCThreads,如果你没有设置该参数,该参数jvm会默认设置成online的cpu的核数但并不包括被shutdown的cpu的核数。

Linux 下获取online的cpu的核数

[cpp]   view plain copy
  1. int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN);  

前面曾经提到过GC的主要过程是由VMThread线程执行,在执行parallel的时候,VMThread线程实际上是在等待线程执行结果的,这也就是为什么并没有设置ParallelGCThreads= online cpu core-1


Parallel gc 和CMS里的young区都可以设置ParallelGCThreads参数,下面主要讲的是Parallel GC

1. Parallel Threads 初始化

Parallel Threads本质是由gcTaskManager管理的gcTaskThread, 在parallelScavenge.cpp 中的initialize() 方法中通过GCTaskManager::create(ParallelGCThreads);初始化gcTaskThread的,也就是当jvm启动的时候thread 就已经创建并且运行。

参数:BindGCTaskThreadsToCPUs

 jvm在linux中并没有对gcTaskThread线程绑定到固定的cpu上,但在solaris上却支持了,请参考函数os::distribute_processes 和 os::bind_to_processor


2. GCTaskQueue

这是一个存放的元素是GCTask的链表结构,指针_insert_end指向最后一个元素,指针_remove_end指向第一个元素

GCTask本身也是一个链表结构,指针_newer指向后面的元素,_older指向前面一个元素,可以参考下面的图

读书笔记之inside JVM(5)

当增加一个GCTask C的时候,会从Queue中取出_insert_end的GCTask B,设置B _newer 为C, 设置C older_为B,设置_insert_end为C

当取一个GCTask的时候,从Queue取出_remove_end的GCTask B, 设置_remove_end为C,设置C的old为null, 设置 B的new为nul


参数 UseGCTaskAffinity

通过设置_affinity 属性用于表示该task的是由哪个线程执行,当GCTaskThread运行的时候会取出该线程的所对应的task

做法使用轮询从queue的第一个开始顺序查找,一直找到然后移除。


3. GCTaskThread 运行

GCTaskThread不停轮询GCTaskQueue,当Queue里面没有数据的时候,线程会wait等待,直到有数据插入notify线程,由于Queue的操作并不是线程安全的,需要在增加和删除的时候申请互斥锁MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag);


VMthread 线程在Parallel GC 时候将任务添加到queue中,然后等待结果。

 

你可能感兴趣的:(读书笔记之inside JVM(5))