《JVM G1源码分析和调优》 —3.3 慢速分配

举报
华章计算机 发表于 2019/12/20 13:46:30 2019/12/20
【摘要】 本节书摘来自华章计算机《JVM G1源码分析和调优》 一书中第3章,第3.3.1节,作者是彭成寒。

3.3 慢速分配

当不能进行快速分配,就进入到慢速分配。实际上在TLAB中也有可能进入到慢速分配,就是我们前面提到的attempt_allocation,前面已经解释过。

这里的慢速分配是指在TLAB中经过努力分配还不能成功,再次进入慢速分配,我们来看一下这个更慢的慢速分配:

attempt_allocation尝试进行对象分配,如果成功则返回。值得注意的是在attempt_

allocation里面可能会进行垃圾回收,这里的垃圾回收是指增量的垃圾回收,主要是新生代或者混合收集,关于收集的内容将在下面的章节介绍,分配相关的代码在3.2节已经介绍过了,不再赘述。

如果大对象在attempt_allocation_humongous,直接分配的老生代。

如果分配不成功,则进行GC垃圾回收,注意这里的回收主要是Full GC,然后再分配。因为这里是分配的最后一步,所以进行几次不同的垃圾回收和尝试。主要代码在satisfy_failed_allocation中。

最终成功分配或者失败达到一定次数,则分配失败。

慢速分配代码如下所示:

HeapWord* G1CollectedHeap::mem_allocate(size_t word_size,

                              bool*  gc_overhead_limit_was_exceeded) {

  for (uint try_count = 1, gclocker_retry_count = 0; /* we'll return */;

    try_count += 1) {

    uint gc_count_before;

 

    HeapWord* result = NULL;

    if (!isHumongous(word_size)) {

      result = attempt_allocation(word_size, &gc_count_before, &gclocker_

        retry_count);

    } else {

      result = attempt_allocation_humongous(word_size, &gc_count_before,

        &gclocker_retry_count);

    }

    if (result != NULL)      return result;

 

    // 进行最后的分配尝试,要做Full GC

    VM_G1CollectForAllocation op(gc_count_before, word_size);

 

    // 通过VMThread执行

    VMThread::execute(&op);

 

    if (op.prologue_succeeded() && op.pause_succeeded()) {

      HeapWord* result = op.result();

      if (result != NULL && !isHumongous(word_size))

        dirty_young_block(result, word_size);

      return result;

    } else {

      // 是否分配失败次数达到阈值

      if (gclocker_retry_count > GCLockerRetryAllocationCount)  return NULL;

 

    }

  }

 

  ShouldNotReachHere();

  return NULL;

}

3.3.1 大对象分配

大对象分配和TLAB中的慢速分配基本类似。唯一的区别就是对象大小不同。步骤主要:

尝试垃圾回收,这里主要是增量回收,同时启动并发标记。

尝试开始分配对象,对于大对象分为两类,一类是大于HeapRegionSize的一半,但是小于HeapRegionSize,即一个完整的堆分区可以保存,则直接从空闲列表直接拿一个堆分区,或者分配一个新的堆分区。如果是连续对象,则需要多个堆分区,思路同上,但是处理的时候需要加锁。

如果失败再次尝试垃圾回收,之后再分配。

最终成功分配或者失败达到一定次数,则分配失败。

大对象分配代码如下所示:

hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp

 

HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size,

                                        uint* gc_count_before_ret,

                                        uint* gclocker_retry_count_ret) {

 

  // 尝试开始垃圾回收

  if (g1_policy()->need_to_start_conc_mark("concurrent humongous allocation",

    word_size)) {

    collect(GCCause::_g1_humongous_allocation);

  }

 

  HeapWord* result = NULL;

  for (int try_count = 1; /* we'll return */; try_count += 1) {

{

      // 需要加锁

      MutexLockerEx x(Heap_lock);

 

      // 大对象分配

      result = humongous_obj_allocate(word_size, AllocationContext::current());

      if (result != NULL)         return result;

   

      if (GC_locker::is_active_and_needs_gc()) {

        should_try_gc = false;

      } else {

        if (GC_locker::needs_gc()) {

          should_try_gc = false;

        } else {

          // 可以继续执行GC

          gc_count_before = total_collections();

          should_try_gc = true;

        }

      }

    }

 

    if (should_try_gc) {

      // 垃圾回收,增量回收

      result = do_collection_pause(word_size, gc_count_before, &succeeded,

                                   GCCause::_g1_humongous_allocation);

      if (result != NULL)       return result;

 

      if (succeeded) {

        // 稍后可以进行回收,可以先返回

        MutexLockerEx x(Heap_lock);

        *gc_count_before_ret = total_collections();

        return NULL;

      }

    } else {

      // 是否达到分配次数阈值

      if (*gclocker_retry_count_ret > GCLockerRetryAllocationCount) {

        MutexLockerEx x(Heap_lock);

        *gc_count_before_ret = total_collections();

        return NULL;

      }

      GC_locker::stall_until_clear();

      (*gclocker_retry_count_ret) += 1;

    }

 

  }

 

  ShouldNotReachHere();

  return NULL;

}


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。