`
小滔哥
  • 浏览: 58889 次
  • 性别: 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完成后返回。

 

分享到:
评论

相关推荐

    jvm 参数及gc详解

    - Parallel GC:多线程版本的Serial GC,提高了吞吐量。 - CMS(Concurrent Mark Sweep)GC:并行标记,低延迟,适用于响应时间敏感的应用。 - G1(Garbage-First)GC:新一代的垃圾收集器,目标是达到可预测的...

    JDK9GC-cheatsheet.pdf

    在这个版本中,HotSpot JVM提供了多种垃圾回收(GC)算法,每种算法都有其特定的适用场景和参数调整策略。下面将详细讨论JDK9中的主要GC类型、相关参数以及调优策略。 1. **GC类型与启用参数**: - **Serial GC**...

    Hotspot源码

    Hotspot源码是Java开发中的重要参考资料,它揭示了Java虚拟机(JVM)的工作原理。Oracle JDK中的Hotspot JVM是Java程序运行的核心,负责字节码解释、编译优化以及垃圾回收等关键任务。Hotspot源码的分析有助于开发者...

    hotspot-virtual-machine-garbage-collection-tuning-guide.pdf

    2. **并行GC(Parallel GC)**:与串行GC相比,它在多处理器机器上通过使用多个线程进行垃圾收集,从而显著提高了GC速度。然而,Stop-The-World事件仍然存在,可能导致应用暂停时间较长。 3. **并发标记GC...

    【译】Java 14 Hotspot 虚拟机垃圾回收调优指南(csdn)————程序.pdf

    Java HotSpot VM 提供了两种主要的优化目标:最大暂停时间和应用吞吐量。最大暂停时间目标通过 `-XX:MaxGCPauseMillis` 参数设定,目标是限制垃圾收集暂停的最长时间,以保证应用程序的响应性。吞吐量目标则关注整个...

    Java高级面试JVM虚拟机、内存结构、垃圾回收机制

    HotSpot VM还包括了两种类型的JIT编译:标准编译用于长时间运行的方法,而OSR编译则用于优化循环中的代码,确保程序运行流畅。 JVM的内存结构分为堆内存、方法区、栈内存、程序计数器和本地方法栈等几个主要部分。...

    Java GC on HP-UX Itanium

    - 分析应用的内存和CPU使用情况,选择合适的GC算法(如Serial、Parallel、Concurrent Mark Sweep或G1)。 - 调整新生代和老年代的比例,以及Survivor空间大小。 - 监控和调整JVM的停顿时间目标(如使用`-XX:...

    JVM历史发展和内存回收笔记

    - **Parallel GC**:多线程版本的Serial GC,提高性能。 - **Concurrent Mark Sweep (CMS)**:并发标记清除,降低停顿时间,适用于响应时间敏感的应用。 - **G1 GC**:并行、并发且目标停顿时间可预测,适用于...

    Inside J2VM.zip

    5. **线程与并发**:JVM提供了多线程支持,包括线程创建、同步、通信等。理解锁、条件变量、信号量等并发原语,以及synchronized、volatile、java.util.concurrent包下的工具类,对于编写高效并发程序非常重要。 6....

    openjdk7源码

    此外,Hotspot还包含垃圾回收(Garbage Collection, GC)机制,如Parallel GC、Concurrent Mark Sweep (CMS) GC和G1 GC等,用于自动管理内存。 在OpenJDK7的源码中,你可能会看到如下几个关键部分: 1. **HotSpot ...

    JVM内存管理面试常见问题全解.doc

    HotSpot VM 是 Sun JDK 和 OpenJDK 中所带的虚拟机,也是目前使用范围最广的 Java 虚拟机。只要装有 JVM 的平台,都可以运行 Java 程序。 JVM 由三个组成部分组成:类加载系统、运行时数据区和执行引擎。类加载系统...

    Princeton Edu - HotspotOverview.pdf

    最后,文档中提到了Hotspot JVM支持的多种线程类型,包括Java线程和JVM线程。JVM线程包含用于垃圾回收、去优化、监视器等的线程。此外,还提到了不同代码(解释执行、编译执行和本地执行)之间的接口差异,即ABI...

    openjdk8源码

    9. **Garbage Collection (GC)**:OpenJDK8实现了多种垃圾收集器,如Parallel GC, G1 GC等,这些都在`sun.gc.*`和`jdk.internal.vm.gc.*`包下。 通过深入研究OpenJDK8源码,开发者可以更好地理解Java平台的运行机制...

    JVM历史发展和内存回收笔记.rar

    2. **Parallel GC**:多线程并行收集,提高吞吐量。 3. **CMS(Concurrent Mark Sweep)GC**:并发标记清除,减少STW(Stop-the-World)暂停时间。 4. **G1(Garbage-First)GC**:目标是实现低延迟,能够预测和控制...

    Java虚拟机

    全书共分为五大部分,围绕内存管理、执行子系统、程序编译与优化、高效并发等核心主题对JVM进行了全面而深入的分析,深刻揭示了JVM的工作原理。第一部分从宏观的角度介绍了整个Java技术体系、Java和JVM的发展历程、...

    openjdk-8-src-b132-03_mar_2014源码

    2. 垃圾收集器:OpenJDK 8提供了多种垃圾收集器,如Serial、Parallel、G1等,源码位于`hotspot/src/share/vm/runtime/gc_*`目录下。 3. JIT编译器:HotSpot虚拟机的Just-In-Time(JIT)编译器在`hotspot/src/share/...

    优化java堆大小的5个技巧.docx

    开发者应详细阅读各个JVM厂商的文档,如Sun HotSpot VM、IBM VM和Oracle JRockit VM,以便更好地了解内存管理机制。 2. **评估应用程序需求**: 应用程序的静态内存占用需求,包括部署的应用数量、加载的类、数据...

    java SE6性能白皮书(PDF中文)

    - HotSpot VM中的DTrace探测器 - 新的监控、管理和诊断功能 - 在Solaris OS上使用Java SE6的可观察性 - **基准测试** - SPECjbb2005 - VolanoMark™ 2.5 综上所述,Java SE6通过多种方式提升了其性能表现,...

    java JVM详解

    - **HotSpot VM**:这是一个高性能的 JVM 实现,支持 JIT 编译技术,能够显著提高 Java 程序的执行速度。 - **JRockit VM**:面向企业级应用的高性能 JVM。 ##### 2.3 SUN JVM 内存管理 (优化) SUN JVM 提供了详细...

Global site tag (gtag.js) - Google Analytics