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

    目录

一、CollectorPolicy

1、定义

2、构造方法 / initialize_all /post_heap_initialize

3、satisfy_failed_metadata_allocation

二、 GCPolicyCounters

三、AdaptiveSizePolicy

四、GenCollectorPolicy

1、构造方法 /initialize_flags /initialize_size_info

2、mem_allocate_work

3、satisfy_failed_allocation


      在《Hotspot 垃圾回收之CollectedHeap 源码解析》中讲解了CollectedHeap的类继承关系,从本篇博客开始会依次讲解CollectedHeap的子类SharedHeap和GenCollectedHeap依赖的相关基础设施类的定义和实现,先把相关的关键基础类学习完成,最后再学习GenCollectedHeap的方法实现。因为某些类在不同GC算法下有不同的实现子类且子类的实现大多跟该GC算法本身的数据结构强耦合,为了方便理解,从本篇博客开始重点关注CMS算法用到的相关子类实现,先完整的学习CMS算法的实现再考虑其他算法下特定子类的实现。

一、CollectorPolicy

1、定义

     CollectorPolicy的定义在hotspot/src/share/vm/memory/collectorPolicy.hpp中,该类及其子类用于定义垃圾回收器使用的全局属性,并初始化分代内存及其他共享资源。CollectorPolicy定义的属性如下:

  • GCPolicyCounters* _gc_policy_counters; //跟踪分代内存的性能的计数器
  • size_t _initial_heap_byte_size; //初始堆内存
  • size_t _max_heap_byte_size; //最大堆内存
  • size_t _min_heap_byte_size; //最小堆内存
  • size_t _space_alignment; //space分配粒度
  • size_t _heap_alignment; //heap分配粒度,_heap_alignment必须大于_space_alignment,且是_space_alignment的整数倍
  • bool _max_heap_size_cmdline; //是否通过命令行参数设置了最大堆内存
  • AdaptiveSizePolicy* _size_policy; //用来自适应调整堆内存大小的策略实现
  • bool _should_clear_all_soft_refs; //是否需要清除所有的软引用,当软引用清除结束,垃圾回收器会将其置为false
  • bool _all_soft_refs_clear; //当GC刚清除完所有的软引用时会设置该属性为true,当返回mutator时被设置成false

CollectorPolicy的类继承关系如下:

CollectorPolicy定义了一个枚举Name来描述子类的名称,如下:

 通过kind方法可以获取当前子类的类型,如下:

  CollectorPolicy定义的方法中除属性读写相关的,获取子类类型相关的方法是非虚方法外,大部分方法都是虚方法,重点关注其初始化方法的实现。

2、构造方法 / initialize_all /post_heap_initialize

     这三个方法都是用来构造并初始化CollectorPolicy,构造方法调用后就会调用initialize_all方法,当CollectedHeap初始化结束后就会调用post_heap_initialize对已初始化的参数做必要的更新。initialize_all和post_heap_initialize的调用链如下:

 post_heap_initialize方法没有给出默认实现,另外两方法的实现如下:

CollectorPolicy::CollectorPolicy() :
    _space_alignment(0),
    _heap_alignment(0),
    _initial_heap_byte_size(InitialHeapSize), //InitialHeapSize表示初始堆内存,单位字节
    _max_heap_byte_size(MaxHeapSize), //MaxHeapSize表示最大堆内存,单位字节
    _min_heap_byte_size(Arguments::min_heap_size()),
    _max_heap_size_cmdline(false),
    _size_policy(NULL),
    _should_clear_all_soft_refs(false),
    _all_soft_refs_clear(false)
{}

 virtual void initialize_all() {
    //initialize_alignments用来初始化分代内存及内存分配相关属性的,没有默认实现
    initialize_alignments();
    //initialize_flags主要用于校验参数的合法性,并设置相关参数
    initialize_flags();
    initialize_size_info();
  }

void CollectorPolicy::initialize_flags() {
  //校验_space_alignment和_heap_alignment的合法性,_heap_alignment必须大于_space_alignment,且是_space_alignment的整数倍
  assert(_space_alignment != 0, "Space alignment not set up properly");
  assert(_heap_alignment != 0, "Heap alignment not set up properly");
  assert(_heap_alignment >= _space_alignment,
         err_msg("heap_alignment: " SIZE_FORMAT " less than space_alignment: " SIZE_FORMAT,
                 _heap_alignment, _space_alignment));
  assert(_heap_alignment % _space_alignment == 0,
         err_msg("heap_alignment: " SIZE_FORMAT " not aligned by space_alignment: " SIZE_FORMAT,
                 _heap_alignment, _space_alignment));

  //如果MaxHeapSize是通过命令行参数配置的
  if (FLAG_IS_CMDLINE(MaxHeapSize)) {
    //校验MaxHeapSize和InitialHeapSize的合法性
    if (FLAG_IS_CMDLINE(InitialHeapSize) && InitialHeapSize > MaxHeapSize) {
      vm_exit_during_initialization("Initial heap size set to a larger value than the maximum heap size");
    }
    if (_min_heap_byte_size != 0 && MaxHeapSize < _min_heap_byte_size) {
      vm_exit_during_initialization("Incompatible minimum and maximum heap sizes specified");
    }
    _max_heap_size_cmdline = true;
  }

  // M是一个常量,表示1M
  if (InitialHeapSize < M) { 
    vm_exit_during_initialization("Too small initial heap");
  }
  if (_min_heap_byte_size < M) {
    vm_exit_during_initialization("Too small minimum heap");
  }

  // User inputs from -Xmx and -Xms must be aligned
  _min_heap_byte_size = align_size_up(_min_heap_byte_size, _heap_alignment);
  //将内存对齐
  uintx aligned_initial_heap_size = align_size_up(InitialHeapSize, _heap_alignment);
  uintx aligned_max_heap_size = align_size_up(MaxHeapSize, _heap_alignment);

  //如果原来的设置值没有对齐,则修改原来设置的值
  if (aligned_initial_heap_size != InitialHeapSize) {
    FLAG_SET_ERGO(uintx, InitialHeapSize, aligned_initial_heap_size);
  }
  if (aligned_max_heap_size != MaxHeapSize) {
    FLAG_SET_ERGO(uintx, MaxHeapSize, aligned_max_heap_size);
  }

  //校验参数合法性,必要时修改
  if (FLAG_IS_CMDLINE(InitialHeapSize) && _min_heap_byte_size != 0 &&
      InitialHeapSize < _min_heap_byte_size) {
    vm_exit_during_initialization("Incompatible minimum and initial heap sizes specified");
  }
  if (!FLAG_IS_DEFAULT(InitialHeapSize) && InitialHeapSize > MaxHeapSize) {
    FLAG_SET_ERGO(uintx, MaxHeapSize, InitialHeapSize);
  } else if (!FLAG_IS_DEFAULT(MaxHeapSize) && InitialHeapSize > MaxHeapSize) {
    FLAG_SET_ERGO(uintx, InitialHeapSize, MaxHeapSize);
    if (InitialHeapSize < _min_heap_byte_size) {
      _min_heap_byte_size = InitialHeapSize;
    }
  }

  _initial_heap_byte_size = InitialHeapSize;
  _max_heap_byte_size = MaxHeapSize;

  //MinHeapDeltaBytes参数表示堆内存空间因为GC而发生变动的最小值
  FLAG_SET_ERGO(uintx, MinHeapDeltaBytes, align_size_up(MinHeapDeltaBytes, _space_alignment));

  DEBUG_ONLY(CollectorPolicy::assert_flags();)
}

void CollectorPolicy::initialize_size_info() {
  if (PrintGCDetails && Verbose) {
    //打印日志
    gclog_or_tty->print_cr("Minimum heap " SIZE_FORMAT "  Initial heap "
      SIZE_FORMAT "  Maximum heap " SIZE_FORMAT,
      _min_heap_byte_size, _initial_heap_byte_size, _max_heap_byte_size);
  }

  DEBUG_ONLY(CollectorPolicy::assert_size_info();)
}

#define FLAG_IS_CMDLINE(name)         (CommandLineFlagsEx::is_cmdline(FLAG_MEMBER(name)))

3、satisfy_failed_metadata_allocation

     satisfy_failed_metadata_allocation是当Metaspace分配内存失败后调用的,用来清理Metaspace空间并尝试重新分配的,该方法的调用链如下:

 Metaspace::allocate中的调用如下图:

其源码实现如下:

MetaWord* CollectorPolicy::satisfy_failed_metadata_allocation(
                                                 ClassLoaderData* loader_data,
                                                 size_t word_size,
                                                 Metaspace::MetadataType mdtype) {
  uint loop_count = 0;
  uint gc_count = 0;
  uint full_gc_count = 0;
  //当前线程没有拥有Heap_lock
  assert(!Heap_lock->owned_by_self(), "Should not be holding the Heap_lock");

  do {
    MetaWord* result = NULL;
    //is_active表示有线程处在JNI关键区中,不能执行GC,needs_gc表示需要GC
    if (GC_locker::is_active_and_needs_gc()) {
      //尝试去扩展Metaspace并分配
      result =
        loader_data->metaspace_non_null()->expand_and_allocate(word_size,
                                                               mdtype);
      if (result != NULL) {
        //分配成功
        return result;
      }
      JavaThread* jthr = JavaThread::current();
      //如果当前线程不在JNI关键区中
      if (!jthr->in_critical()) {
        // 阻塞直到所有处在JNI关键区中的线程从JNI关键区中退出,最后一个从关键区退出的线程会在退出时触发GC,参考GC_Locker的实现
        GC_locker::stall_until_clear();
        //GC已经执行,重新循环尝试分配,GC执行完成后is_active_and_needs_gc会返回false
        continue;
      } else {
        if (CheckJNICalls) {
          fatal("Possible deadlock due to allocating while"
                " in jni critical section");
        }
        //线程在关键区中不能GC,所以分配失败,返回NULL
        return NULL;
      }
    }

    //GC完成后下一次循环is_active_and_needs_gc返回false,就会进入此逻辑
    {  //获取Heap_lock,读取GC的累计次数
      MutexLocker ml(Heap_lock);
      gc_count      = Universe::heap()->total_collections();
      full_gc_count = Universe::heap()->total_full_collections();
    }
    
    //如果之前有线程处于关键区中,则通过VM_CollectForMetadataAllocation会执行内存分配
    //如果之前没有线程处于关键区中,则通过VM_CollectForMetadataAllocation触发GC并在GC完成后分配内存
    //在VM_CollectForMetadataAllocation 执行的时候会判断是否需要跳过此次GC,因为退出关键区的线程已经触发了GC,但是有可能短期内又没内存了需要再次GC
    VM_CollectForMetadataAllocation op(loader_data,
                                       word_size,
                                       mdtype,
                                       gc_count,
                                       full_gc_count,
                                       GCCause::_metadata_GC_threshold);
    //当前线程会被阻塞直到VM_CollectForMetadataAllocation被VMThread执行完成
    VMThread::execute(&op);

    //VM_CollectForMetadataAllocation已经执行完成,gc_locked为true说明此次GC后依然没有足够的内存,需要再次循环再次GC
    if (op.gc_locked()) {
      continue;
    }
    //gc_locked返回false,说明跳过了本次GC或者GC正常已完成内存分配
    //prologue_succeeded返回false表示会跳过此次GC,VM_CollectForMetadataAllocation中的doit方法不会被执行,即不会分配内存,返回true表示会执行内存分配,分配失败再执行GC
    if (op.prologue_succeeded()) {
      return op.result();
    }
    //prologue_succeeded返回false继续循环
    loop_count++;
    //QueuedAllocationWarningCount表示循环了多少次数后打印warning日志,默认为o
    if ((QueuedAllocationWarningCount > 0) &&
        (loop_count % QueuedAllocationWarningCount == 0)) {
      warning("satisfy_failed_metadata_allocation() retries %d times \n\t"
              " size=" SIZE_FORMAT, loop_count, word_size);
    }
  } while (true);  // Until a GC is done
}

二、 GCPolicyCounters

      GCPolicyCounters的定义在hotspot/src/share/vm/gc_implementation/shared/gCPolicyCounters.hpp中,用来统计GC过程中各种性能数据。GCPolicyCounters的类继承关系如下:

 GCPolicyCounters也定义了一个枚举Name来描述子类,如下:

GCPolicyCounters定义的属性只有三个,如下:

 

子类定义的属性就就很多了,而且是跟特定算法数据结构相关的属性,以GCAdaptivePolicyCounters为例,如下:

 GCPolicyCounters及其子类定义的方法都是读写这些属性的,没有复杂逻辑。

三、AdaptiveSizePolicy

      AdaptiveSizePolicy定义在hotspot/src/share/vm/gc_implementation/shared/adaptiveSizePolicy.hpp中,用于保存GC停顿和耗时等统计数据,并基于这些数据计算出各分代内存区的大小,从而实现动态的自适应调整各分代内存的大小,其类继承关系如下:

AdaptiveSizePolicy同样定义了一个描述子类的枚举GCPolicyKind,如下:

 AdaptiveSizePolicy定义的属性基本都是各种维度的统计指标,比如GC耗时,GC间隔时间,GC停顿时间等,如下图:

具体指标的用途需要结合相关方法的调用来理解,这里暂且不做深入探讨。

四、GenCollectorPolicy

      GenCollectorPolicy继承自CollectorPolicy,表示分代内存使用的CollectorPolicy,同样定义在collectorPolicy.hpp中,增加了如下属性:

  • size_t _min_gen0_size;  //gen0的内存最小值
  • size_t _initial_gen0_size; //gen0的内存初始值
  • size_t _max_gen0_size; //gen0的内存最大值
  • size_t _gen_alignment; //分代内存分配粒度,_gen_alignment必须被_space_alignment整除,_heap_alignment被_gen_alignment整除
  • GenerationSpec **_generations; //一种特殊的Generation实现

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

1、构造方法 /initialize_flags /initialize_size_info

     这三个都是GenCollectorPolicy的初始化方法,改写了父类的默认实现,除计算各种属性值外还需要初始化所有的Generation,如下:

GenCollectorPolicy::GenCollectorPolicy() :
    _min_gen0_size(0),
    _initial_gen0_size(0),
    _max_gen0_size(0),
    _gen_alignment(0),
    _generations(NULL)
{}

 virtual void initialize_generations() { };

  virtual void initialize_all() {
    CollectorPolicy::initialize_all();
    initialize_generations();
  }

void GenCollectorPolicy::initialize_flags() {
  //调用父类方法初始化父类属性
  CollectorPolicy::initialize_flags();
  //校验_gen_alignment参数的合法性,_gen_alignment必须被_space_alignment整除,_heap_alignment被_gen_alignment整除
  assert(_gen_alignment != 0, "Generation alignment not set up properly");
  assert(_heap_alignment >= _gen_alignment,
         err_msg("heap_alignment: " SIZE_FORMAT " less than gen_alignment: " SIZE_FORMAT,
                 _heap_alignment, _gen_alignment));
  assert(_gen_alignment % _space_alignment == 0,
         err_msg("gen_alignment: " SIZE_FORMAT " not aligned by space_alignment: " SIZE_FORMAT,
                 _gen_alignment, _space_alignment));
  assert(_heap_alignment % _gen_alignment == 0,
         err_msg("heap_alignment: " SIZE_FORMAT " not aligned by gen_alignment: " SIZE_FORMAT,
                 _heap_alignment, _gen_alignment));

  // All generational heaps have a youngest gen; handle those flags here

  // Make sure the heap is large enough for two generations
  //young_gen_size_lower_bound方法返回年轻代的内存最小值
  uintx smallest_new_size = young_gen_size_lower_bound();
  //老年代只有一个区,将老年代的最小值加上年轻代的最小值,按照_heap_alignment向上取整就是堆内存的最小值
  uintx smallest_heap_size = align_size_up(smallest_new_size + align_size_up(_space_alignment, _gen_alignment),
                                           _heap_alignment);
  //重置MaxHeapSize                                         
  if (MaxHeapSize < smallest_heap_size) {
    FLAG_SET_ERGO(uintx, MaxHeapSize, smallest_heap_size);
    _max_heap_byte_size = MaxHeapSize;
  }
  // If needed, synchronize _min_heap_byte size and _initial_heap_byte_size
  if (_min_heap_byte_size < smallest_heap_size) {
    _min_heap_byte_size = smallest_heap_size;
    //重置InitialHeapSize
    if (InitialHeapSize < _min_heap_byte_size) {
      FLAG_SET_ERGO(uintx, InitialHeapSize, smallest_heap_size);
      _initial_heap_byte_size = smallest_heap_size;
    }
  }

  // NewSize表示一个新的generation(分代内存区)的初始内存值
  smallest_new_size = MAX2(smallest_new_size, (uintx)align_size_down(NewSize, _gen_alignment));
  if (smallest_new_size != NewSize) {
    // 没有使用FLAG_SET_ERGO重置,因为该参数可能通过命令行被改写
    NewSize = smallest_new_size;
  }
  _initial_gen0_size = NewSize;
   
  //MaxNewSize表示表示一个新的generation(分代内存区)的最大内存值
  //如果MaxNewSize不是默认值
  if (!FLAG_IS_DEFAULT(MaxNewSize)) {
    uintx min_new_size = MAX2(_gen_alignment, _min_gen0_size);

    if (MaxNewSize >= MaxHeapSize) {
      // Make sure there is room for an old generation
      uintx smaller_max_new_size = MaxHeapSize - _gen_alignment;
      if (FLAG_IS_CMDLINE(MaxNewSize)) {
        warning("MaxNewSize (" SIZE_FORMAT "k) is equal to or greater than the entire "
                "heap (" SIZE_FORMAT "k).  A new max generation size of " SIZE_FORMAT "k will be used.",
                MaxNewSize/K, MaxHeapSize/K, smaller_max_new_size/K);
      }
      //重置MaxNewSize
      FLAG_SET_ERGO(uintx, MaxNewSize, smaller_max_new_size);
      if (NewSize > MaxNewSize) {
        //重置NewSize
        FLAG_SET_ERGO(uintx, NewSize, MaxNewSize);
        _initial_gen0_size = NewSize;
      }
    } else if (MaxNewSize < min_new_size) {
      FLAG_SET_ERGO(uintx, MaxNewSize, min_new_size);
    } else if (!is_size_aligned(MaxNewSize, _gen_alignment)) {
      FLAG_SET_ERGO(uintx, MaxNewSize, align_size_down(MaxNewSize, _gen_alignment));
    }
    _max_gen0_size = MaxNewSize;
  }

  if (NewSize > MaxNewSize) {
    // At this point this should only happen if the user specifies a large NewSize and/or
    // a small (but not too small) MaxNewSize.
    if (FLAG_IS_CMDLINE(MaxNewSize)) {
      warning("NewSize (" SIZE_FORMAT "k) is greater than the MaxNewSize (" SIZE_FORMAT "k). "
              "A new max generation size of " SIZE_FORMAT "k will be used.",
              NewSize/K, MaxNewSize/K, NewSize/K);
    }
    //重置MaxNewSize
    FLAG_SET_ERGO(uintx, MaxNewSize, NewSize);
    _max_gen0_size = MaxNewSize;
  }
  //SurvivorRatio表示eden/survivor的倍数,默认是8
  //NewRatio表示老年代与年轻代的内存空间比例,默认是2
  if (SurvivorRatio < 1 || NewRatio < 1) {
    vm_exit_during_initialization("Invalid young gen ratio specified");
  }

  DEBUG_ONLY(GenCollectorPolicy::assert_flags();)
}

//返回年轻代的内存最小值
size_t GenCollectorPolicy::young_gen_size_lower_bound() {
  //年轻代包含三个区,eden+two survivors,所以这里是3
  return align_size_up(3 * _space_alignment, _gen_alignment);
}

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

  size_t max_new_size = 0;
  if (!FLAG_IS_DEFAULT(MaxNewSize)) {
    max_new_size = MaxNewSize;
  } else {
    //按照NewRatio计算允许的新的generation的内存最大值
    max_new_size = scale_by_NewRatio_aligned(_max_heap_byte_size);
    max_new_size = MIN2(MAX2(max_new_size, NewSize), MaxNewSize);
  }
  assert(max_new_size > 0, "All paths should set max_new_size");


  if (_max_heap_byte_size == _min_heap_byte_size) {
    _min_gen0_size = max_new_size;
    _initial_gen0_size = max_new_size;
    _max_gen0_size = max_new_size;
  } else {
    size_t desired_new_size = 0;
    //通过命令行设置的
    if (FLAG_IS_CMDLINE(NewSize)) {
      // If NewSize is set on the command line, we must use it as
      // the initial size and it also makes sense to use it as the
      // lower limit.
      _min_gen0_size = NewSize;
      desired_new_size = NewSize;
      max_new_size = MAX2(max_new_size, NewSize);
    } else if (FLAG_IS_ERGO(NewSize)) { //通过代码计算重置的
      // If NewSize is set ergonomically, we should use it as a lower
      // limit, but use NewRatio to calculate the initial size.
      _min_gen0_size = NewSize;
      desired_new_size =
        MAX2(scale_by_NewRatio_aligned(_initial_heap_byte_size), NewSize);
      max_new_size = MAX2(max_new_size, NewSize);
    } else {
      //NewSize是默认值
      _min_gen0_size = MAX2(scale_by_NewRatio_aligned(_min_heap_byte_size), NewSize);
      desired_new_size =
        MAX2(scale_by_NewRatio_aligned(_initial_heap_byte_size), NewSize);
    }

    assert(_min_gen0_size > 0, "Sanity check");
    _initial_gen0_size = desired_new_size;
    _max_gen0_size = max_new_size;

    // Bound the sizes by the corresponding overall heap sizes.
    _min_gen0_size = bound_minus_alignment(_min_gen0_size, _min_heap_byte_size);
    _initial_gen0_size = bound_minus_alignment(_initial_gen0_size, _initial_heap_byte_size);
    _max_gen0_size = bound_minus_alignment(_max_gen0_size, _max_heap_byte_size);

  
    // Final check min <= initial <= max
    _min_gen0_size = MIN2(_min_gen0_size, _max_gen0_size);
    _initial_gen0_size = MAX2(MIN2(_initial_gen0_size, _max_gen0_size), _min_gen0_size);
    _min_gen0_size = MIN2(_min_gen0_size, _initial_gen0_size);
  }

  //重置NewSize
  if (NewSize != _initial_gen0_size) {
    FLAG_SET_ERGO(uintx, NewSize, _initial_gen0_size);
  }

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

  if (PrintGCDetails && Verbose) {
    gclog_or_tty->print_cr("1: Minimum gen0 " SIZE_FORMAT "  Initial gen0 "
      SIZE_FORMAT "  Maximum gen0 " SIZE_FORMAT,
      _min_gen0_size, _initial_gen0_size, _max_gen0_size);
  }

  DEBUG_ONLY(GenCollectorPolicy::assert_size_info();)
}

size_t GenCollectorPolicy::scale_by_NewRatio_aligned(size_t base_size) {
  return align_size_down_bounded(base_size / (NewRatio + 1), _gen_alignment);
}

size_t GenCollectorPolicy::bound_minus_alignment(size_t desired_size,
                                                 size_t maximum_size) {
  size_t max_minus = maximum_size - _gen_alignment;
  return desired_size < max_minus ? desired_size : max_minus;
}

2、mem_allocate_work

     mem_allocate_work方法用于从Java堆中分配指定大小的内存块,并在必要时触发GC,具体的GC通过VM_GenCollectForAllocation完成,其调用链如下:

GenCollectedHeap中的两个调用方法都是直接调用mem_allocate_work,以mem_allocate为例,如下:

该方法的实现如下:

HeapWord* GenCollectorPolicy::mem_allocate_work(size_t size,
                                        bool is_tlab,
                                        bool* gc_overhead_limit_was_exceeded) {
  //获取Java堆对象                                      
  GenCollectedHeap *gch = GenCollectedHeap::heap();

  debug_only(gch->check_for_valid_allocation_state());
  //校验当前没有GC
  assert(gch->no_gc_in_progress(), "Allocation during gc not allowed");

  //默认情况下gc_overhead_limit_was_exceeded是false,只有当GC耗时超过限制了才会置为true
  *gc_overhead_limit_was_exceeded = false;

  HeapWord* result = NULL;

  //不断循环尝试直到分配成功或者触发GC
  for (uint try_count = 1, gclocker_stalled_count = 0; /* return or throw */; try_count += 1) {
    //通过析构函数自动丢弃掉在每次循环过程中分配的Handle
    HandleMark hm; // discard any handles allocated in each iteration

    //获取年轻代
    Generation *gen0 = gch->get_gen(0);
    //校验其是否支持线性分配方式分配一段地址连续的内存
    assert(gen0->supports_inline_contig_alloc(),
      "Otherwise, must do alloc within heap lock");
    //是否应该从该Generation分配,如果size过大则应该在老年代分配
    if (gen0->should_allocate(size, is_tlab)) {
      //Generation内部加锁,分配指定大小的内存
      result = gen0->par_allocate(size, is_tlab);
      if (result != NULL) {
        //分配成功,is_in_reserved方法检查某个指针是否在堆内存范围内
        assert(gch->is_in_reserved(result), "result not in heap");
        return result;
      }
    }
    //如果不能在年轻代分配或者年轻代分配失败
    uint gc_count_before;  // read inside the Heap_lock locked region
    {
      //获取Heap_lock锁
      MutexLocker ml(Heap_lock);
      if (PrintGC && Verbose) {
        gclog_or_tty->print_cr("TwoGenerationCollectorPolicy::mem_allocate_work:"
                      " attempting locked slow path allocation");
      }
      //first_only表示是否只尝试一次,should_try_older_generation_allocation方法返回是否尝试在老年代分配
      //只有大对象才会在老年代分配
      bool first_only = ! should_try_older_generation_allocation(size);
      //遍历所有的Generation尝试分配
      result = gch->attempt_allocation(size, is_tlab, first_only);
      if (result != NULL) {
        //分配成功
        assert(gch->is_in_reserved(result), "result not in heap");
        return result;
      }
      
      //在其他的Generation中同样分配失败
      //有线程处于JNI关键区且需要GC
      if (GC_locker::is_active_and_needs_gc()) {
        if (is_tlab) {
           //如果TLAB,调用方会自动重试,其他线程会触发GC,所以这里返回NULL
          return NULL;  // Caller will retry allocating individual object
        }
        //如果某个Generation还能扩展
        if (!gch->is_maximal_no_gc()) {
          //尝试扩展Java堆并分配
          result = expand_heap_and_allocate(size, is_tlab);
          if (result != NULL) {
            //分配成功
            return result;
          }
        }
        
        //所有Generation都不能扩展了,只能GC了
        //GCLockerRetryAllocationCount表示当因为GC锁被阻塞了,尝试分配内存的次数,默认值是2
        if (gclocker_stalled_count > GCLockerRetryAllocationCount) {
          return NULL; // we didn't get to do a GC and we didn't get any memory
        }

        //如果这个线程不在JNI关键区,则会阻塞当前线程,等待所有处于JNI关键区的线程从关键区中退出了,最后一个退出的线程会触发GC,避免触发更多的没必要的GC。
        JavaThread* jthr = JavaThread::current();
        //判断当前线程是否处于JNI关键区内
        if (!jthr->in_critical()) {
          MutexUnlocker mul(Heap_lock);
          //阻塞当前线程直到处于JNI关键区的线程从关键区中退出了
          GC_locker::stall_until_clear();
          gclocker_stalled_count += 1;
          //GC结束,在下一次循环中尝试分配内存
          continue;
        } else {
          //如果当前线程在JNI关键区内,CheckJNICalls表示是否校验JNI调用的参数,默认为false
          if (CheckJNICalls) {
            fatal("Possible deadlock due to allocating while"
                  " in jni critical section");
          }
          //返回NULL,因为处于关键区中的线程没法GC,没法继续分配内存
          return NULL;
        }
      }

      //获取GC的累计次数
      gc_count_before = Universe::heap()->total_collections();
    }
    
    //处于关键区的线程退出了并执行GC了依然分配失败
    //没有线程处于关键区中,分配失败,触发GC
    VM_GenCollectForAllocation op(size, is_tlab, gc_count_before);
    //当前线程会被阻塞直到GC执行完成
    VMThread::execute(&op);
    //如果执行了GC,返回false表示该次GC被跳过了,可能其他线程执行了GC
    if (op.prologue_succeeded()) {
      result = op.result();
      //gc_locked返回true表示GC后依然分配失败,需要再次GC
      if (op.gc_locked()) {
         assert(result == NULL, "must be NULL if gc_locked() is true");
         continue;  // retry and/or stall as necessary
      }

      //GC完成且内存分配成功,如果GC耗时超过限制了则会返回NULL并抛出异常,然后重置gc_overhead_limit_exceeded
      //这样gc_overhead_limit_exceeded不会持久化
      //gc_overhead_limit_exceeded返回是否GC耗时超过限制
      const bool limit_exceeded = size_policy()->gc_overhead_limit_exceeded();
      //all_soft_refs_clear返回是否清楚了所有的软引用
      const bool softrefs_clear = all_soft_refs_clear();

      if (limit_exceeded && softrefs_clear) {
        *gc_overhead_limit_was_exceeded = true;
        //重置gc_overhead_limit_exceeded属性
        size_policy()->set_gc_overhead_limit_exceeded(false);
        if (op.result() != NULL) {
          //填充对象,因为GC耗时超了,为了避免后期GC耗时越来越长,所以认为内存分配失败,需要抛出异常
          CollectedHeap::fill_with_object(op.result(), size);
        }
        return NULL;
      }
      assert(result == NULL || gch->is_in_reserved(result),
             "result not in heap");
      return result;
    }
    //本次GC被跳过了,其他线程执行了GC,再次循环尝试分配内存
    //QueuedAllocationWarningCount表示尝试了一定次数后就打印warning日志,默认值是0
    if ((QueuedAllocationWarningCount > 0) &&
        (try_count % QueuedAllocationWarningCount == 0)) {
          warning("TwoGenerationCollectorPolicy::mem_allocate_work retries %d times \n\t"
                  " size=" SIZE_FORMAT " %s", try_count, size, is_tlab ? "(TLAB)" : "");
    }
  }
}


//是否应该尝试在老年代分配
bool GenCollectorPolicy::should_try_older_generation_allocation(
        size_t word_size) const {
  GenCollectedHeap* gch = GenCollectedHeap::heap();
  //获取年轻代的当前可用空间
  size_t gen0_capacity = gch->get_gen(0)->capacity_before_gc();
  return    (word_size > heap_word_size(gen0_capacity))
         || GC_locker::is_active_and_needs_gc()
         || gch->incremental_collection_failed();
}

HeapWord* GenCollectorPolicy::expand_heap_and_allocate(size_t size,
                                                       bool   is_tlab) {
  GenCollectedHeap *gch = GenCollectedHeap::heap();
  HeapWord* result = NULL;
  //遍历所有的Generation
  for (int i = number_of_generations() - 1; i >= 0 && result == NULL; i--) {
    Generation *gen = gch->get_gen(i);
    if (gen->should_allocate(size, is_tlab)) {
      //如果Generation可以分配,则尝试扩展并分配
      result = gen->expand_and_allocate(size, is_tlab);
    }
  }
  assert(result == NULL || gch->is_in_reserved(result), "result not in heap");
  return result;
}

bool all_soft_refs_clear() { return _all_soft_refs_clear; }

上述代码中涉及的JNI关键区的概念可以参考《Hotspot JNIEnv API详解(二)》

3、satisfy_failed_allocation

    satisfy_failed_allocation就是上一个方法mem_allocate_work内存分配失败时执行GC的VM_GenCollectForAllocation的底层实现,该方法的调用链如下:

VM_GenCollectForAllocation::doit方法的调用如下:

该方法的实现如下:

HeapWord* GenCollectorPolicy::satisfy_failed_allocation(size_t size,
                                                        bool   is_tlab) {
  //获取Java堆                                                      
  GenCollectedHeap *gch = GenCollectedHeap::heap();
  //初始化GCCause
  GCCauseSetter x(gch, GCCause::_allocation_failure);
  HeapWord* result = NULL;

  assert(size != 0, "Precondition violated");
  if (GC_locker::is_active_and_needs_gc()) {
    // GC_locker是active说明还有线程处于JNI关键区未开始GC
    if (!gch->is_maximal_no_gc()) {
      result = expand_heap_and_allocate(size, is_tlab);
    }
    return result;   // could be null if we are out of space
  } else if (!gch->incremental_collection_will_fail(false /* don't consult_young */)) {
    //incremental_collection_will_fail表示如果执行增量收集是否会失败,参考上一次增量收集的结果
    //如果可以执行增量收集
    gch->do_collection(false            /* full */,
                       false            /* clear_all_soft_refs */,
                       size             /* size */,
                       is_tlab          /* is_tlab */,
                       number_of_generations() - 1 /* max_level */);
  } else {
    if (Verbose && PrintGCDetails) {
      gclog_or_tty->print(" :: Trying full because partial may fail :: ");
    }
    //执行全量的full GC
    gch->do_collection(true             /* full */,
                       false            /* clear_all_soft_refs */,
                       size             /* size */,
                       is_tlab          /* is_tlab */,
                       number_of_generations() - 1 /* max_level */);
  }
  //尝试分配内存
  result = gch->attempt_allocation(size, is_tlab, false /*first_only*/);

  if (result != NULL) {
    //分配成功
    assert(gch->is_in_reserved(result), "result not in heap");
    return result;
  }

  //分配失败,尝试扩展Java堆并分配内存
  result = expand_heap_and_allocate(size, is_tlab);
  if (result != NULL) {
    return result;
  }

  //分配失败,说明已经内存不足了,这时需要强制清理软引用,强制压实Java堆,任何可能的获取可用内存的方法都会执行,尽管代价昂贵
  //如果尝试失败,抛出OOM异常
  {
    UIntFlagSetting flag_change(MarkSweepAlwaysCompactCount, 1); // Make sure the heap is fully compacted

    gch->do_collection(true             /* full */,
                       true             /* clear_all_soft_refs */,
                       size             /* size */,
                       is_tlab          /* is_tlab */,
                       number_of_generations() - 1 /* max_level */);
  }

  //再次尝试分配内存
  result = gch->attempt_allocation(size, is_tlab, false /* first_only */);
  if (result != NULL) {
    assert(gch->is_in_reserved(result), "result not in heap");
    return result;
  }
  //分配失败,软引用已经清理的,标志位被置为false
  assert(!should_clear_all_soft_refs(),
    "Flag should have been handled and cleared prior to this point");

  return NULL;
}

 bool should_clear_all_soft_refs() { return _should_clear_all_soft_refs; }

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值