`
小滔哥
  • 浏览: 58110 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

Hotspot VM的Parallel GC 中 GC线程是如何创建与工作的

    博客分类:
  • JVM
 
阅读更多
  • 1.源码中WorkGang, GangWorker结构关系

SharedHeap的结构如下,在结构中含有成员FlexibleWorkGang* _workers;

class SharedHeap : public CollectedHeap {
  friend class VMStructs;
  friend class VM_GC_Operation;
  friend class VM_CGC_Operation;
private:
  // For claiming strong_roots tasks.
  SubTasksDone* _process_strong_tasks;
protected:
  ……
  // If we're doing parallel GC, use this gang of threads.
  FlexibleWorkGang* _workers;
  ……
}; 


FlexibleWorkGang的继承关系如下

class FlexibleWorkGang: public WorkGang {
  ……
};
class WorkGang: public AbstractWorkGang {
  ……
  Void run_task(AbstractGangTask* task);
  ……
};
class AbstractWorkGang: public CHeapObj {
  // Here's the public interface to this class.
public:
  // Constructor and destructor.
  AbstractWorkGang(const char* name, bool are_GC_task_threads,
                   bool are_ConcurrentGC_threads);
  ~AbstractWorkGang();
  // Run a task, returns when the task is done (or terminated).
  virtual void run_task(AbstractGangTask* task) = 0;
  // Stop and terminate all workers.
  virtual void stop();
public:
  // Debugging.
  const char* name() const;
protected:
  // Initialize only instance data.
  const bool _are_GC_task_threads;
  const bool _are_ConcurrentGC_threads;
  // Printing support.
  const char* _name;
  // The monitor which protects these data,
  // and notifies of changes in it.
  Monitor*  _monitor;
  // The count of the number of workers in the gang.
  int _total_workers;
  // Whether the workers should terminate.
  bool _terminate;
  // The array of worker threads for this gang.
  // This is only needed for cleaning up.
  GangWorker** _gang_workers;
  // The task for this gang.
  AbstractGangTask* _task;
  // A sequence number for the current task.
  int _sequence_number;
  // The number of started workers.
  int _started_workers;
  // The number of finished workers.
  int _finished_workers;
}; 



GangWorker的结构如下

class GangWorker: public WorkerThread {
public:
  // Constructors and destructor.
  GangWorker(AbstractWorkGang* gang, uint id);

  // The only real method: run a task for the gang.
  virtual void run();
  // Predicate for Thread
  virtual bool is_GC_task_thread() const;
  virtual bool is_ConcurrentGC_thread() const;
  // Printing
  void print_on(outputStream* st) const;
  virtual void print() const { print_on(tty); }
protected:
  AbstractWorkGang* _gang;

  virtual void initialize();
  virtual void loop();

public:
  AbstractWorkGang* gang() const { return _gang; }
}; 

 

  • 2. GC线程创建

GangWorker就是GC线程,那么它是如何创建起来的呢?
当虚拟机启动的时候,会进行一些初始化操作,我们看一看是通过怎样的路径创建GangWorker的。Trace信息如下所示

#0  0x00002adb4964bace in FlexibleWorkGang (this=0x48,
    name=0xb00000000 <Address 0xb00000000 out of bounds>, workers=10971,
    are_GC_task_threads=8, are_ConcurrentGC_threads=false)
    at /home/chengtao/hotspot20/src/share/vm/utilities/workgroup.hpp:280
#1  0x00002adb4998e403 in SharedHeap (this=0x5903a430, policy_=0x5903a2f0)
    at /home/chengtao/hotspot20/src/share/vm/memory/sharedHeap.cpp:76
#2  0x00002adb49731331 in GenCollectedHeap (this=0x5903a430, policy=0x5903a2f0)
    at /home/chengtao/hotspot20/src/share/vm/memory/genCollectedHeap.cpp:76
#3  0x00002adb49a23f95 in Universe::initialize_heap ()
    at /home/chengtao/hotspot20/src/share/vm/memory/universe.cpp:921
#4  0x00002adb49a243f8 in universe_init ()
    at /home/chengtao/hotspot20/src/share/vm/memory/universe.cpp:781
#5  0x00002adb49767a52 in init_globals ()
    at /home/chengtao/hotspot20/src/share/vm/runtime/init.cpp:98
#6  0x00002adb49a0e56a in Threads::create_vm (args=0x41964080,
    canTryAgain=0x4196405b)
    at /home/chengtao/hotspot20/src/share/vm/runtime/thread.cpp:3092
#7  0x00002adb497a181c in JNI_CreateJavaVM (vm=0x419640e0, penv=0x419640e8,
    args=0x41964080)
    at /home/chengtao/hotspot20/src/share/vm/prims/jni.cpp:3344
#8  0x00000000400035f8 in InitializeJVM ()
#9  0x000000004000206e in JavaMain ()


在WorkGang进行完成构造以后,会进行初始化,调用_workers->initialize_workers();在这个函数中,进行创建GangWorker,具体如下

//创建保存GangWork的指针数组
_gang_workers = NEW_C_HEAP_ARRAY(GangWorker*, total_workers());

 

// 创建_total_workers个GangWorker,实际上_total_workers是与ParallelGCThreads相等的,ParallelGCThreads这个值是根据cpu core计算出来的一个值
//每创建一个GangWorker就会立即启动这个线程
for (int worker = 0; worker < total_workers(); worker += 1) {
    GangWorker* new_worker = allocate_worker(worker);
    assert(new_worker != NULL, "Failed to allocate GangWorker");
    _gang_workers[worker] = new_worker;
    if (new_worker == NULL || !os::create_thread(new_worker, worker_type)) {
      vm_exit_out_of_memory(0, "Cannot create worker GC thread. Out of system resources.");
      return false;
    }
    if (!DisableStartThread) {
      os::start_thread(new_worker);
    }
  }


到此,Parallel GC线程就被创建完了,并启动。

  • 3. Parallel GC线程是如何工作的

我们来看一下GangWorker的一些函数,可以看到,当GC线程被创建以后,首先会进行一些初始化工作,然后就进入到了loop函数中。仔细分析这个loop函数,可以发现,刚开始GC线程是进入wait状态的,等待被唤醒然后进行GC,接下来我们看一看GC线程是如何被唤醒的。

void GangWorker::run() {
  initialize();
  loop();
}
void GangWorker::initialize() {
  this->initialize_thread_local_storage();
  assert(_gang != NULL, "No gang to run in");
  os::set_priority(this, NearMaxPriority);
  if (TraceWorkGang) {
    tty->print_cr("Running gang worker for gang %s id %d",
                  gang()->name(), id());
  }
  // The VM thread should not execute here because MutexLocker's are used
  // as (opposed to MutexLockerEx's).
  assert(!Thread::current()->is_VM_thread(), "VM thread should not be part"
         " of a work gang");
}

void GangWorker::loop() {
  int previous_sequence_number = 0;
  Monitor* gang_monitor = gang()->monitor();
  for ( ; /* !terminate() */; ) {
    WorkData data;
    int part;  // Initialized below.
    {
      // Grab the gang mutex.
      MutexLocker ml(gang_monitor);
      // Wait for something to do.
      // Polling outside the while { wait } avoids missed notifies
      // in the outer loop.
      gang()->internal_worker_poll(&data);
      if (TraceWorkGang) {
        tty->print("Polled outside for work in gang %s worker %d",
                   gang()->name(), id());
        tty->print("  terminate: %s",
                   data.terminate() ? "true" : "false");
        tty->print("  sequence: %d (prev: %d)",
                   data.sequence_number(), previous_sequence_number);
        if (data.task() != NULL) {
          tty->print("  task: %s", data.task()->name());
        } else {
          tty->print("  task: NULL");
        }
        tty->cr();
      }
      for ( ; /* break or return */; ) {
        // Terminate if requested.
        if (data.terminate()) {
          gang()->internal_note_finish();
          gang_monitor->notify_all();
          return;
        }
        // Check for new work.
        if ((data.task() != NULL) &&
            (data.sequence_number() != previous_sequence_number)) {
          gang()->internal_note_start();
          gang_monitor->notify_all();
          part = gang()->started_workers() - 1;
          break;
        }
        // Nothing to do.
        gang_monitor->wait(/* no_safepoint_check */ true);
        gang()->internal_worker_poll(&data);
        if (TraceWorkGang) {
          tty->print("Polled inside for work in gang %s worker %d",
                     gang()->name(), id());
          tty->print("  terminate: %s",
                     data.terminate() ? "true" : "false");
          tty->print("  sequence: %d (prev: %d)",
                     data.sequence_number(), previous_sequence_number);
          if (data.task() != NULL) {
            tty->print("  task: %s", data.task()->name());
          } else {
            tty->print("  task: NULL");
          }
          tty->cr();
        }
      }
      // Drop gang mutex.
    }
    if (TraceWorkGang) {
      tty->print("Work for work gang %s id %d task %s part %d",
                 gang()->name(), id(), data.task()->name(), part);
    }
    assert(data.task() != NULL, "Got null task");
    data.task()->work(part);
    {
      if (TraceWorkGang) {
        tty->print("Finish for work gang %s id %d task %s part %d",
                   gang()->name(), id(), data.task()->name(), part);
      }
      // Grab the gang mutex.
      MutexLocker ml(gang_monitor);
      gang()->internal_note_finish();
      // Tell the gang you are done.
      gang_monitor->notify_all();
      // Drop the gang mutex.
    }
    previous_sequence_number = data.sequence_number();
  }
}



当需要进行GC的时候,以ParNew为例,会进入到ParNewGeneration::collect这个函数,这个函数中有这样一段代码

  if (n_workers > 1) {
    GenCollectedHeap::StrongRootsScope srs(gch);
    workers->run_task(&tsk);
  } else {
    GenCollectedHeap::StrongRootsScope srs(gch);
    tsk.work(0);
  }


  进行Parallel GC的时候,就会进入到workers->run_task(&tsk)这个函数中了。函数原型就是void WorkGang::run_task(AbstractGangTask* task);
在这个函数中有这样一段代码

  // Initialize.
  _task = task;
  _sequence_number += 1;
  _started_workers = 0;
  _finished_workers = 0;
  // Tell the workers to get to work.
  monitor()->notify_all();
  // Wait for them to be finished
  while (finished_workers() < total_workers()) {
    if (TraceWorkGang) {
      tty->print_cr("Waiting in work gang %s: %d/%d finished sequence %d",
                    name(), finished_workers(), total_workers(),
                    _sequence_number);
    }
    monitor()->wait(/* no_safepoint_check */ true);
  }
  _task = NULL;


    从代码中我们可以发现,monitor()->notify_all();就可以唤醒GC线程了,然后进入到wait状态,等待所有GC线程完成GC任务结束,然后退出。

  • 4. 总结

总结起来很简单,就是初始时,WorkGang创建ParallelGCThreads个GC线程(GangWorker),这些GC线程处于wait状态。当进行Paralel GC的时候,WorkGang就会唤醒wait的GC线程,进行Parallel GC。WorkGang等待Parallel GC完成后返回。

 

分享到:
评论

相关推荐

    hllvm.HotSpot VM Serial GC的一个问题1

    実装​》第4章4.4小节​. 每个版本的算法描述都稍微不同,我的伪代码也跟这两本书写的方式稍微不同,但背后要表达的核心思想是一样的就OK了.Tracing GC

    Troubleshooting Guide for Java SE 6 with HotSpot VM

    Troubleshooting Guide for Java SE 6 with HotSpot VM

    The Java HotSpot VM.pdf

    The Java HotSpot VM.pdf

    Java HotSpot VM Options

    jvm参数介绍,oracle HotSpot官方参数文档。

    深入Hotspot源码与Linux内核理解NIO与Netty线程模型.pdf

    深入Hotspot源码与Linux内核理解NIO与Netty线程模型

    Hotspot VM源码

    HotSpot正是目前世界上java虚拟机的最好的实现。 HotSpot的基础代码是许多人辛勤劳动的结晶,这个过程迄今已持续了超过10年的时间(当然时间长并不意味着一定好,一半一半吧)。所以到现在为止,他的体积是很大的。...

    hotspot.tar.gz

    官方完整版JVM源码Hotspot VM,文件名hotspot.tar.gz。官方完整版JVM源码Hotspot VM,文件名hotspot.tar.gz。

    Java Performance Companion(Addison,2016)

    The authors, who are all leading Java performance and Java HotSpot VM experts, help you improve performance by using modern software engineering practices, avoiding common mistakes, and applying tips ...

    HotSpot线程实现-线程创建1

    1. Java API层:该层次就是我们最常接触的,平时使用Thread类,就是对应于这一个层次 2. JVM层:每一个Thread实例,在调用其start0

    借HSDB来探索HotSpot VM的运行时数据.gist1

    It will be set after the class is loaded.VM Started: Set deferred breakpoint Tes

    HotSpot GC官网文档截图 - 20200917

    官网英文文档介绍GC的各个版本发展、GC选取、调优参数等; 当前压缩包为官网文档截图文件集合,个人觉得资源珍贵且比网上各个帖子更有参考价值,故存储自用并分享。

    java-jdk-hotspot源码

    学习JDK 源码必备,提起HotSpot VM,相信所有Java程序员都知道,它是Sun JDK和OpenJDK中所带的虚拟机,也是目前使用范围最广的Java虚拟机。 但不一定所有人都知道的是,这个目前看起来“血统纯正”的虚拟机在最初...

    Java.Performance.Companion.0133796825

    The authors, who are all leading Java performance and Java HotSpot VM experts, help you improve performance by using modern software engineering practices, avoiding common mistakes, and applying tips ...

    JAVA虚拟机精讲

    比如:字节码的编译原理、字节码的内部组成结构、通过源码的方式剖析HotSpot VM 的启动过程和初始化过程、Java 虚拟机的运行时内存、垃圾收集算法、垃圾收集器(重点讲解了Serial 收集器、ParNew 收集器、Parallel ...

    借HSDB来探索HotSpot VM的运行时数据1

    DR版回答是:t1在存Java静态变量的地方, 概念上在JVM的方法区(method area)里t2在Java堆里, 作为Test的一个实例的字段存在t3在J

    Java虚拟机精讲.高翔龙.带书签完整版.pdf

    比如:字节码的编译原理、字节码的内部组成结构、通过源码的方式剖析HotSpot VM 的启动过程和初始化过程、Java 虚拟机的运行时内存、垃圾收集算法、垃圾收集器(重点讲解了Serial 收集器、ParNew 收集器、Parallel ...

    JAVA虚拟机精讲 pdf

    比如:字节码的编译原理、字节码的内部组成结构、通过源码的方式剖析HotSpot VM 的启动过程和初始化过程、Java 虚拟机的运行时内存、垃圾收集算法、垃圾收集器(重点讲解了Serial 收集器、ParNew 收集器、Parallel ...

    Compilation in the HotSpot VM-Zoltan-Majo.pdf

    技术文档分享。

    hotspot源码

    提起HotSpot VM,相信所有Java程序员都知道,它是Sun JDK和OpenJDK中所带的虚拟机,也是目前使用范围最广的Java虚拟机。

Global site tag (gtag.js) - Google Analytics