Hotspot 垃圾回收之CollectorPolicy (二) 源码解析

目录

一、GC_locker

1、lock_critical / unlock_critical

2、check_active_before_gc

3、stall_until_clear

二、TwoGenerationCollectorPolicy

三、ConcurrentMarkSweepPolicy

四、ASConcurrentMarkSweepPolicy

五、GenerationSpec


本篇继续上一篇《Hotspot 垃圾回收之CollectorPolicy (一) 源码解析》讲解CollectorPolicy的其他子类及其相关类的实现。

一、GC_locker

      GC_locker的定义在hotspot/src/share/vm/memory/gCLocker.hpp中,用来跟JNICritical_lock配合使用,实现当线程处于JNI关键区的时候不能执行GC的功能,该类定义的属性只有三个,如下:

  • static volatile jint _jni_lock_count;  //处于JNI关键区的线程个数
  • static volatile bool _needs_gc;  //Java堆内存不足,需要GC
  • static volatile bool _doing_gc;   //是否在执行GC

重点关注以下方法的实现。

1、lock_critical / unlock_critical

      lock_critical和unlock_critical是配对使用的,在needs_gc方法返回false,即不需要GC时两者都是走快速路径执行,只增加当前线程的关键区计数器;当needs_gc方法返回true,即Java堆空间不足,需要GC时,如果是一个新的线程进入关键区则走慢速路径,会阻塞该线程进入关键区,直到其他在关键区中的线程从关键区退出,否则依然走快速路径;如果该线程的关键区计数器等于1了则走慢速路径,获取锁,减少线程和GC_locker的关键区计数,并且如果该线程还是最后一个从关键区退出的线程,该线程会触发GC并等待GC完成。这两方法的源码如下:

inline void GC_locker::lock_critical(JavaThread* thread) {
  //如果不在关键区
  if (!thread->in_critical()) {
    if (needs_gc()) {
      //needs_gc返回true,则通过慢速加锁的方式增加关键区计数,如果需要GC则阻塞当前线程进入关键区
      jni_lock(thread);
      return;
    }
    //非debug模式下为空实现
    increment_debug_jni_lock_count();
  }
  //增加当前线程的关键区计数,因为只增加当前线程的计数所以操作块
  thread->enter_critical();
}

inline void GC_locker::unlock_critical(JavaThread* thread) {
  //如果当前线程的关键区计数等于1
  if (thread->in_last_critical()) {
    if (needs_gc()) {
      //如果需要GC则走慢速方式触发GC 
      jni_unlock(thread);
      return;
    }
    //生产模式下为空实现
    decrement_debug_jni_lock_count();
  }
  //减少线程的关键区计数
  thread->exit_critical();
}

void GC_locker::jni_lock(JavaThread* thread) {
  assert(!thread->in_critical(), "shouldn't currently be in a critical region");
  //获取锁JNICritical_lock
  MutexLocker mu(JNICritical_lock);
  //如果有一个线程处于关键区且需要GC,则阻塞当前线程进入关键区
  //之所以执行while循环检查,是因为执行wait方法后当有一个线程从关键区退出就会唤起所有阻塞的线程
  while (is_active_and_needs_gc() || _doing_gc) {
    JNICritical_lock->wait();
  }
  //增加线程的关键区计数
  thread->enter_critical();
  //增加GC_locker的关键区计数
  _jni_lock_count++;
  increment_debug_jni_lock_count();
}

void GC_locker::jni_unlock(JavaThread* thread) {
  //校验当前线程的关键区计数等于1
  assert(thread->in_last_critical(), "should be exiting critical region");
  //获取锁
  MutexLocker mu(JNICritical_lock);
  //GC_locker的计数减1
  _jni_lock_count--;
  //生产模式下为空实现
  decrement_debug_jni_lock_count();
  //线程的关键区计数减1
  thread->exit_critical();
  //如果需要GC并且所有线程从关键区退出
  if (needs_gc() && !is_active_internal()) {
    //当前线程是最后一个退出关键区的线程,触发GC
    _doing_gc = true;
    {
      //放弃锁JNICritical_lock
      MutexUnlocker munlock(JNICritical_lock);
      if (PrintJNIGCStalls && PrintGCDetails) {
        ResourceMark rm; // JavaThread::name() allocates to convert to UTF8
        gclog_or_tty->print_cr("%.3f: Thread \"%s\" is performing GC after exiting critical section, %d locked",
            gclog_or_tty->time_stamp().seconds(), Thread::current()->name(), _jni_lock_count);
      }
      //触发GC
      Universe::heap()->collect(GCCause::_gc_locker);
    }
    //GC完成
    _doing_gc = false;
    _needs_gc = false;
    JNICritical_lock->notify_all();
  }
}

static bool needs_gc()       { return _needs_gc;                        }

  // Shorthand
static bool is_active_and_needs_gc() {
    return needs_gc() && is_active_internal();
  }

static bool is_active_internal() {
    //生产模式下是空实现
    verify_critical_count();
    return _jni_lock_count > 0;
  }

这两方法的调用链如下:

以获取基本类型数组元素指针的jni_GetPrimitiveArrayCritical和jni_ReleasePrimitiveArrayCritical方法为例说明,如下:

JNI_ENTRY(void*, jni_GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy))
  JNIWrapper("GetPrimitiveArrayCritical");

  DTRACE_PROBE3(hotspot_jni, GetPrimitiveArrayCritical__entry, env, array, isCopy);
  //让当前线程进入关键区
  GC_locker::lock_critical(thread);
  if (isCopy != NULL) {
    *isCopy = JNI_FALSE;
  }
  oop a = JNIHandles::resolve_non_null(array);
  assert(a->is_array(), "just checking");
  BasicType type;
  //获取元素类型
  if (a->is_objArray()) {
    type = T_OBJECT;
  } else {
    type = TypeArrayKlass::cast(a->klass())->element_type();
  }
  //获取数组元素的指针
  void* ret = arrayOop(a)->base(type);

  DTRACE_PROBE1(hotspot_jni, GetPrimitiveArrayCritical__return, ret);

  return ret;
JNI_END

JNI_ENTRY(void, jni_ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode))
  JNIWrapper("ReleasePrimitiveArrayCritical");

  DTRACE_PROBE4(hotspot_jni, ReleasePrimitiveArrayCritical__entry, env, array, carray, mode);
  //退出关键区
  GC_locker::unlock_critical(thread);

  DTRACE_PROBE(hotspot_jni, ReleasePrimitiveArrayCritical__return);

JNI_END

 JavaThread中关键区计数的实现如下:

2、check_active_before_gc

     _needs_gc是决定lock_critical / unlock_critical方法走快速还是慢速执行路径的关键属性,全局搜索该属性的相关方法,如下:

jni_unlock方法中会在GC结束后将_needs_gc置为false,另外两个need_gc和needs_gc_address都是读取_needs_gc属性的,重点关注将_needs_gc置为true的check_active_before_gc方法,该方法用于在GC前检查是否有线程处于JNI关键区内,如果有则必须等待这些处于关键区的线程从关键区中退出并且将need_gc置为true,如果没有线程处于JNI关键区内则可直接执行GC,该方法的实现如下:

bool GC_locker::check_active_before_gc() {
  //校验当前JVM处于安全点上
  assert(SafepointSynchronize::is_at_safepoint(), "only read at safepoint");
  //如果有线程处于关键区内,并且_needs_gc为false
  //如果没有线程处于关键区内则可以直接执行GC
  if (is_active() && !_needs_gc) {
    verify_critical_count();
    //将_needs_gc置为true
    _needs_gc = true;
    if (PrintJNIGCStalls && PrintGCDetails) {
      ResourceMark rm; // JavaThread::name() allocates to convert to UTF8
      gclog_or_tty->print_cr("%.3f: Setting _needs_gc. Thread \"%s\" %d locked.",
                             gclog_or_tty->time_stamp().seconds(), Thread::current()->name(), _jni_lock_count);
    }

  }
  return is_active();
}

static bool is_active() {
    assert(SafepointSynchronize::is_at_safepoint(), "only read at safepoint");
    return is_active_internal();
}

static bool is_active_internal() {
    verify_critical_count();
    return _jni_lock_count > 0;
  }

以GenCollectedHeap::do_collection中的调用为例说明,如下图:

3、stall_until_clear

      stall_until_clear该方法用于阻塞当前进程直到所有处于关键区中的线程从关键区中退出,调用链如下:

该方法的实现如下:

void GC_locker::stall_until_clear() {
  //校验当前线程不在关键区内
  assert(!JavaThread::current()->in_critical(), "Would deadlock");
  //获取锁JNICritical_lock
  MutexLocker   ml(JNICritical_lock);

  if (needs_gc()) {
    if (PrintJNIGCStalls && PrintGCDetails) {
      ResourceMark rm; // JavaThread::name() allocates to convert to UTF8
      gclog_or_tty->print_cr("%.3f: Allocation failed. Thread \"%s\" is stalled by JNI critical section, %d locked.",
                             gclog_or_tty->time_stamp().seconds(), Thread::current()->name(), _jni_lock_count);
    }
  }

  //不断循环直到needs_gc返回false,nedd_gc只有在所有线程所有关键区退出并且GC完成后才会置为false
  while (needs_gc()) {
    JNICritical_lock->wait();
  }
}

二、TwoGenerationCollectorPolicy

       TwoGenerationCollectorPolicy继承自GenCollectorPolicy,其定义也在collectorPolicy.hpp中,表示一个只有两个Generation的CollectorPolicy,现有的GenCollectedHeap的所有子类都是只有两个Generation,第一个Generation相同,对应GenCollectorPolicy中新增的gen0的相关属性,第二个Generation的实现各不相同。TwoGenerationCollectorPolicy增加了第二个Generation即gen1相关的属性,如下:

因为要初始化这三个属性, TwoGenerationCollectorPolicy改写了父类的initialize_flags和initialize_size_info方法,其实现如下:

void TwoGenerationCollectorPolicy::initialize_flags() {
  //调用父类方法
  GenCollectorPolicy::initialize_flags();
  //OldSize表示老年代的大小,如果OldSize没有按照_gen_alignment对齐
  if (!is_size_aligned(OldSize, _gen_alignment)) {
    //重置OldSize
    FLAG_SET_ERGO(uintx, OldSize, align_size_down(OldSize, _gen_alignment));
  }
  
  //如果OldSize通过命令行设置,MaxHeapSize是默认值
  if (FLAG_IS_CMDLINE(OldSize) && FLAG_IS_DEFAULT(MaxHeapSize)) {
    // 根据NewRatio和OldSize计算允许的堆内存大小
    assert(NewRatio > 0, "NewRatio should have been set up earlier");
    size_t calculated_heapsize = (OldSize / NewRatio) * (NewRatio + 1);
    calculated_heapsize = align_size_up(calculated_heapsize, _heap_alignment);
    
    //重置MaxHeapSize
    FLAG_SET_ERGO(uintx, MaxHeapSize, calculated_heapsize);
    _max_heap_byte_size = MaxHeapSize;
    FLAG_SET_ERGO(uintx, InitialHeapSize, calculated_heapsize);
    _initial_heap_byte_size = InitialHeapSize;
  }

  // adjust max heap size if necessary
  if (NewSize + OldSize > MaxHeapSize) {
    //如果MaxHeapSize通过命令行设置
    if (_max_heap_size_cmdline) {
      // somebody set a maximum heap size with the intention that we should not
      // exceed it. Adjust New/OldSize as necessary.
      uintx calculated_size = NewSize + OldSize;
      //计算需要缩减的比例,然后计算并重置NewSize
      double shrink_factor = (double) MaxHeapSize / calculated_size;
      uintx smaller_new_size = align_size_down((uintx)(NewSize * shrink_factor), _gen_alignment);
      FLAG_SET_ERGO(uintx, NewSize, MAX2(young_gen_size_lower_bound(), smaller_new_size));
      _initial_gen0_size = NewSize;
      //重置OldSize
      FLAG_SET_ERGO(uintx, OldSize, MaxHeapSize - NewSize);
    } else {
      //不是通过命令行设置,重置MaxHeapSize
      FLAG_SET_ERGO(uintx, MaxHeapSize, align_size_up(NewSize + OldSize, _heap_alignment));
      _max_heap_byte_size = MaxHeapSize;
    }
  }
  //是OopDesc的一个属性,如果true,则执行写Barrier时会将这个对象指针转换成volatile oop
  //保证并发下,oop的修改能够及时在各CPU间同步
  always_do_update_barrier = UseConcMarkSweepGC;

  DEBUG_ONLY(TwoGenerationCollectorPolicy::assert_flags();)
}

void TwoGenerationCollectorPolicy::initialize_size_info() {
  //调用父类方法
  GenCollectorPolicy::initialize_size_info();

  //计算max_gen1_size
  _max_gen1_size = MAX2(_max_heap_byte_size - _max_gen0_size, _gen_alignment);

  //如果OldSize没有通过命令行设置
  if (!FLAG_IS_CMDLINE(OldSize)) {
    // The user has not specified any value but the ergonomics
    // may have chosen a value (which may or may not be consistent
    // with the overall heap size).  In either case make
    // the minimum, maximum and initial sizes consistent
    // with the gen0 sizes and the overall heap sizes.
    _min_gen1_size = MAX2(_min_heap_byte_size - _min_gen0_size, _gen_alignment);
    _initial_gen1_size = MAX2(_initial_heap_byte_size - _initial_gen0_size, _gen_alignment);
    // _max_gen1_size has already been made consistent above
    FLAG_SET_ERGO(uintx, OldSize, _initial_gen1_size);
  } else {
    // It's been explicitly set on the command line.  Use the
    // OldSize and then determine the consequences.
    _min_gen1_size = MIN2(OldSize, _min_heap_byte_size - _min_gen0_size);
    _initial_gen1_size = OldSize;

    // 如果_min_gen1_size+_min_gen0_size小于_min_heap_byte_size
    if ((_min_gen1_size + _min_gen0_size + _gen_alignment) < _min_heap_byte_size) {
      warning("Inconsistency between minimum heap size and minimum "
              "generation sizes: using minimum heap = " SIZE_FORMAT,
              _min_heap_byte_size);
    }
    if (OldSize > _max_gen1_size) {
      warning("Inconsistency between maximum heap size and maximum "
              "generation sizes: using maximum heap = " SIZE_FORMAT
              " -XX:OldSize flag is being ignored",
              _max_heap_byte_size);
    }
    
    //按需调整_min_gen0_size或者_min_gen1_size
    if (adjust_gen0_sizes(&_min_gen0_size, &_min_gen1_size, _min_heap_byte_size)) {
      if (PrintGCDetails && Verbose) {
        gclog_or_tty->print_cr("2: Minimum gen0 " SIZE_FORMAT "  Initial gen0 "
              SIZE_FORMAT "  Maximum gen0 " SIZE_FORMAT,
              _min_gen0_size, _initial_gen0_size, _max_gen0_size);
      }
    }
    // Initial size
    if (adjust_gen0_sizes(&_initial_gen0_size, &_initial_gen1_size,
                          _initial_heap_byte_size)) {
      if (PrintGCDetails && Verbose) {
        gclog_or_tty->print_cr("3: Minimum gen0 " SIZE_FORMAT "  Initial gen0 "
          SIZE_FORMAT "  Maximum gen0 " SIZE_FORMAT,
          _min_gen0_size, _initial_gen0_size, _max_gen0_size);
      }
    }
  }
  //重置参数
  _min_gen1_size = MIN2(_min_gen1_size, _max_gen1_size);

  // Check that min gen1 <= initial gen1 <= max gen1
  _initial_gen1_size = MAX2(_initial_gen1_size, _min_gen1_size);
  _initial_gen1_size = MIN2(_initial_gen1_size, _max_gen1_size);

  // Write back to flags if necessary
  if (NewSize != _initial_gen0_size) {
    FLAG_SET_ERGO(uintx, NewSize, _initial_gen0_size);
  }

  if (MaxNewSize != _max_gen0_size) {
    FLAG_SET_ERGO(uintx, MaxNewSize, _max_gen0_size);
  }

  if (OldSize != _initial_gen1_size) {
    FLAG_SET_ERGO(uintx, OldSize, _initial_gen1_size);
  }

  if (PrintGCDetails && Verbose) {
    gclog_or_tty->print_cr("Minimum gen1 " SIZE_FORMAT "  Initial gen1 "
      SIZE_FORMAT "  Maximum gen1 " SIZE_FORMAT,
      _min_gen1_size, _initial_gen1_size, _max_gen1_size);
  }

  DEBUG_ONLY(TwoGenerationCollectorPolicy::assert_size_info();)
}

bool TwoGenerationCollectorPolicy::adjust_gen0_sizes(size_t* gen0_size_ptr,
                                                     size_t* gen1_size_ptr,
                                                     const size_t heap_size) {
  bool result = false;

  if ((*gen0_size_ptr + *gen1_size_ptr) > heap_size) {
    uintx smallest_new_size = young_gen_size_lower_bound();
    if ((heap_size < (*gen0_size_ptr + _min_gen1_size)) &&
        (heap_size >= _min_gen1_size + smallest_new_size)) {
      // Adjust gen0 down to accommodate _min_gen1_size
      *gen0_size_ptr = align_size_down_bounded(heap_size - _min_gen1_size, _gen_alignment);
      result = true;
    } else {
      *gen1_size_ptr = align_size_down_bounded(heap_size - *gen0_size_ptr, _gen_alignment);
    }
  }
  return result;
}

 always_do_update_barrier属性的调用链如下:

以Klass::klass_oop_store中的调用为例说明,如下:

 如果always_do_update_barrier为true,就将该oop*转成volatile oop*, OrderAccess::release_store_ptr的实现如下:

三、ConcurrentMarkSweepPolicy

      ConcurrentMarkSweepPolicy在hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.hpp中定义的,是UseConcMarkSweepGC为true但是UseAdaptiveSizePolicy为false时使用的CollectorPolicy实现。该类没有添加新的属性,重点关注initialize_alignments 和 initialize_generations方法的实现。这两方法在父类中都没有提供有效实现,两个都是在GenCollectorPolicy::initialize_all方法中调用的,先调用initialize_alignments,最后调用initialize_generations。

void ConcurrentMarkSweepPolicy::initialize_alignments() {
  //初始化_space_alignment等属性
  _space_alignment = _gen_alignment = (uintx)Generation::GenGrain;
  _heap_alignment = compute_heap_alignment();
}

size_t CollectorPolicy::compute_heap_alignment() {
  
  size_t alignment = GenRemSet::max_alignment_constraint(GenRemSet::CardTable);

  if (UseLargePages) {
      // In presence of large pages we have to make sure that our
      // alignment is large page aware.
      alignment = lcm(os::large_page_size(), alignment);
  }

  return alignment;
}

void ConcurrentMarkSweepPolicy::initialize_generations() {
  //初始化_generations
  _generations = NEW_C_HEAP_ARRAY3(GenerationSpecPtr, number_of_generations(), mtGC,
    CURRENT_PC, AllocFailStrategy::RETURN_NULL);
  if (_generations == NULL)
    vm_exit_during_initialization("Unable to allocate gen spec");
  //UseParNewGC表示在新生代使用并发收集,默认为false
  if (UseParNewGC) {
    if (UseAdaptiveSizePolicy) {
      _generations[0] = new GenerationSpec(Generation::ASParNew,
                                           _initial_gen0_size, _max_gen0_size);
    } else {
      _generations[0] = new GenerationSpec(Generation::ParNew,
                                           _initial_gen0_size, _max_gen0_size);
    }
  } else {
    _generations[0] = new GenerationSpec(Generation::DefNew,
                                         _initial_gen0_size, _max_gen0_size);
  }
  //UseAdaptiveSizePolicy表示使用自适应策略动态调整各代的大小,默认为true
  if (UseAdaptiveSizePolicy) {
    _generations[1] = new GenerationSpec(Generation::ASConcurrentMarkSweep,
                            _initial_gen1_size, _max_gen1_size);
  } else {
    _generations[1] = new GenerationSpec(Generation::ConcurrentMarkSweep,
                            _initial_gen1_size, _max_gen1_size);
  }

  if (_generations[0] == NULL || _generations[1] == NULL) {
    //如果初始化失败,则退出
    vm_exit_during_initialization("Unable to allocate gen spec");
  }
}

int number_of_generations()          { return 2; }

四、ASConcurrentMarkSweepPolicy

      ASConcurrentMarkSweepPolicy继承自ConcurrentMarkSweepPolicy,同样定义在cmsCollectorPolicy.hpp中,该类没有新增属性和方法,就改写了父类initialize_gc_policy_counters和kind方法的实现,前者的实现如下:

父类的实现是使用GCPolicyCounters,如下:

 

五、GenerationSpec

      GenerationSpec的定义在hotspot/src/share/vm/memory/generationSpec.hpp中,该类就是用来初始化Generation的,其定义的属性就三个,如下:

_name表示 Generation的类型,另外两个参数表示Generation的初始大小和最大值。GenerationSpec定义的方法不多,重点关注其init方法的实现,如下:

Generation* GenerationSpec::init(ReservedSpace rs, int level,
                                 GenRemSet* remset) {
  switch (name()) {
    case Generation::DefNew:
      return new DefNewGeneration(rs, init_size(), level);

    case Generation::MarkSweepCompact:
      return new TenuredGeneration(rs, init_size(), level, remset);

#if INCLUDE_ALL_GCS
    case Generation::ParNew:
      return new ParNewGeneration(rs, init_size(), level);

    case Generation::ASParNew:
      return new ASParNewGeneration(rs,
                                    init_size(),
                                    init_size() /* min size */,
                                    level);

    case Generation::ConcurrentMarkSweep: {
      assert(UseConcMarkSweepGC, "UseConcMarkSweepGC should be set");
      CardTableRS* ctrs = remset->as_CardTableRS();
      if (ctrs == NULL) {
        vm_exit_during_initialization("Rem set incompatibility.");
      }

      ConcurrentMarkSweepGeneration* g = NULL;
      g = new ConcurrentMarkSweepGeneration(rs,
                 init_size(), level, ctrs, UseCMSAdaptiveFreeLists,
                 (FreeBlockDictionary<FreeChunk>::DictionaryChoice)CMSDictionaryChoice);

      g->initialize_performance_counters();

      return g;
    }

    case Generation::ASConcurrentMarkSweep: {
      assert(UseConcMarkSweepGC, "UseConcMarkSweepGC should be set");
      CardTableRS* ctrs = remset->as_CardTableRS();
      if (ctrs == NULL) {
        vm_exit_during_initialization("Rem set incompatibility.");
      }

      ASConcurrentMarkSweepGeneration* g = NULL;
      g = new ASConcurrentMarkSweepGeneration(rs,
                 init_size(), level, ctrs, UseCMSAdaptiveFreeLists,
                 (FreeBlockDictionary<FreeChunk>::DictionaryChoice)CMSDictionaryChoice);

      g->initialize_performance_counters();

      return g;
    }
#endif // INCLUDE_ALL_GCS

    default:
      guarantee(false, "unrecognized GenerationName");
      return NULL;
  }
}

Generation::Name name()        const { return _name; }

size_t init_size()             const { return _init_size; }

该方法跟ConcurrentMarkSweepPolicy::initialize_generations是配合使用的,其调用链如下:

GenCollectedHeap::initialize()中的调用如下图:

 

_gens就是表示年轻代和老年代的Generation数组了。

发布了117 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_31865983/article/details/103796511