[转帖]Full GC (Ergonomics) 产生的原因

full,gc,ergonomics,产生,原因 · 浏览次数 : 0

小编点评

## Full GC 的Ergonomics 解释 Full GC 的Ergonomics 是指在使用 Parallel Scavenge 收集器时调解 GC 暂停时间和吞吐量的平衡。 **主要参数:** * **-XX:MaxGCPauseMillis:** 控制最大 GC 停顿时间的毫秒值,默认值为 500。 * **-XX:GCTimeRatio:** 控制吞吐时间与 GC 时间的比率,默认值为 0.8。 **参数的作用:** * **-XX:MaxGCPauseMillis:** 如果 GC 停顿时间超过 MaxGCPauseMillis,就会停止 GC。 * **-XX:GCTimeRatio:** 如果 GC 停顿时间超过 GCTimeRatio,就会降低 GCP 速度。 **使用 Full GC 的场景:** * 在使用 Parallel Scavenge 收集器时,为了减少 GC 停顿时间,可以降低 GCP 速度。 * 当需要降低 GCP 速度时,可以提高 GCP 速度。 **其他说明:** * Full GC 是 Allocation Failure 的结果。 * Full GC 的参数通常在 GC 配置中设置。

正文

发生Full GC,有很多种原因,不仅仅是只有Allocation Failure。

还有以下这么多:

 

#include "precompiled.hpp"
#include "gc/shared/gcCause.hpp"

const char* GCCause::to_string(GCCause::Cause cause) {
switch (cause) {
case _java_lang_system_gc:
return "System.gc()";

case _full_gc_alot:
  return "FullGCAlot";

case _scavenge_alot:
  return "ScavengeAlot";

case _allocation_profiler:
  return "Allocation Profiler";

case _jvmti_force_gc:
  return "JvmtiEnv ForceGarbageCollection";

case _gc_locker:
  return "GCLocker Initiated GC";

case _heap_inspection:
  return "Heap Inspection Initiated GC";

case _heap_dump:
  return "Heap Dump Initiated GC";

case _wb_young_gc:
  return "WhiteBox Initiated Young GC";

case _wb_conc_mark:
  return "WhiteBox Initiated Concurrent Mark";

case _wb_full_gc:
  return "WhiteBox Initiated Full GC";

case _update_allocation_context_stats_inc:
case _update_allocation_context_stats_full:
  return "Update Allocation Context Stats";

case _no_gc:
  return "No GC";

case _allocation_failure:
  return "Allocation Failure";

case _tenured_generation_full:
  return "Tenured Generation Full";

case _metadata_GC_threshold:
  return "Metadata GC Threshold";

case _metadata_GC_clear_soft_refs:
  return "Metadata GC Clear Soft References";

case _cms_generation_full:
  return "CMS Generation Full";

case _cms_initial_mark:
  return "CMS Initial Mark";

case _cms_final_remark:
  return "CMS Final Remark";

case _cms_concurrent_mark:
  return "CMS Concurrent Mark";

case _old_generation_expanded_on_last_scavenge:
  return "Old Generation Expanded On Last Scavenge";

case _old_generation_too_full_to_scavenge:
  return "Old Generation Too Full To Scavenge";

case _adaptive_size_policy:
  return "Ergonomics";

case _g1_inc_collection_pause:
  return "G1 Evacuation Pause";

case _g1_humongous_allocation:
  return "G1 Humongous Allocation";

case _dcmd_gc_run:
  return "Diagnostic Command";

case _last_gc_cause:
  return "ILLEGAL VALUE - last gc cause - ILLEGAL VALUE";

default:
  return "unknown GCCause";

}
ShouldNotReachHere();
}

该文JVM内存分配担保机制在后面部分讲到在Server模式下,当设置为3M的时候,偶尔会发生Full GC。注意:是“偶尔”。

另外我们看到日志片段:

[Full GC (Ergonomics) [PSYoungGen: 544K->0K(9216K)] [ParOldGen: 6144K->6627K(10240K)] 6688K->6627K(19456K), [Metaspace: 3286K->3286K(1056768K)], 0.0063048 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]

发现Full GC后面还有一个单词叫Ergonomics,Full GC后面的括号就是本次GC所产生的原因。

上文中我们说到:

发现当我们使用Server模式下的ParallelGC收集器组合(Parallel Scavenge+Serial Old的组合)下,担保机制的实现和之前的Client模式下(SerialGC收集器组合)有所变化。在GC前还会进行一次判断,如果要分配的内存>=Eden区大小的一半,那么会直接把要分配的内存放入老年代中。否则才会进入担保机制。

也就是使用了Parallel Scavenge+Serial Old的组合。

我们就去看看Parallel Scavenge回收策略的源码吧!

以下是片段:

 

// This method contains all heap specific policy for invoking scavenge.
// PSScavenge::invoke_no_policy() will do nothing but attempt to
// scavenge. It will not clean up after failed promotions, bail out if
// we've exceeded policy time limits, or any other special behavior.
// All such policy should be placed here.
//
// Note that this method should only be called from the vm_thread while
// at a safepoint!
bool PSScavenge::invoke() {
  assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint");
  assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread");
  assert(!ParallelScavengeHeap::heap()->is_gc_active(), "not reentrant");

ParallelScavengeHeap* const heap = ParallelScavengeHeap::heap();
PSAdaptiveSizePolicy* policy = heap->size_policy();
IsGCActiveMark mark;

const bool scavenge_done = PSScavenge::invoke_no_policy();
const bool need_full_gc = !scavenge_done ||
policy->should_full_GC(heap->old_gen()->free_in_bytes());
bool full_gc_done = false;

if (UsePerfData) {
PSGCAdaptivePolicyCounters* const counters = heap->gc_policy_counters();
const int ffs_val = need_full_gc ? full_follows_scavenge : not_skipped;
counters->update_full_follows_scavenge(ffs_val);
}

if (need_full_gc) {
GCCauseSetter gccs(heap, GCCause::_adaptive_size_policy);
CollectorPolicy* cp = heap->collector_policy();
const bool clear_all_softrefs = cp->should_clear_all_soft_refs();

if (UseParallelOldGC) {
  full_gc_done = PSParallelCompact::invoke_no_policy(clear_all_softrefs);
} else {
  full_gc_done = PSMarkSweep::invoke_no_policy(clear_all_softrefs);
}

}

return full_gc_done;
}

核心代码:

 

 if (need_full_gc) {
    GCCauseSetter gccs(heap, GCCause::_adaptive_size_policy);
    CollectorPolicy* cp = heap->collector_policy();
    const bool clear_all_softrefs = cp->should_clear_all_soft_refs();
if (UseParallelOldGC) {
  full_gc_done = PSParallelCompact::invoke_no_policy(clear_all_softrefs);
} else {
  full_gc_done = PSMarkSweep::invoke_no_policy(clear_all_softrefs);
}

}

注:基本内容是如果需要full gc那么就进入if块,然后执行full gc逻辑。另外这里的_adaptive_size_policy 常量就是对应的Ergonomics:

 

 

 case _adaptive_size_policy:
      return "Ergonomics";

那么full gc的条件是什么呢?也就是什么情况导致发生了本次full gc呢?

我们继续看看need_full_gc这个常量吧:

full gc条件:

 

const bool need_full_gc = !scavenge_done ||
  policy->should_full_GC(heap->old_gen()->free_in_bytes());

should_ful_GC方法:

 

// If the remaining free space in the old generation is less that
// that expected to be needed by the next collection, do a full
// collection now.
bool PSAdaptiveSizePolicy::should_full_GC(size_t old_free_in_bytes) {

// A similar test is done in the scavenge's should_attempt_scavenge(). If
// this is changed, decide if that test should also be changed.
bool result = padded_average_promoted_in_bytes() > (float) old_free_in_bytes;
//如果晋升到老年代的平均大小大于老年代的剩余大小,则认为要进行一次full gc

log_trace(gc, ergo)(
"%s after scavenge average_promoted "
SIZE_FORMAT " padded_average_promoted "
SIZE_FORMAT " free in old gen " SIZE_FORMAT,
result ? "Full" : "No full",
(size_t) average_promoted_in_bytes(),
(size_t) padded_average_promoted_in_bytes(),
old_free_in_bytes);
return result;
}

通过查看should_full_GC方法,我们发现了这行代码:

 

bool result = padded_average_promoted_in_bytes() > (float) old_free_in_bytes;

通过该行代码,我们知道,如果晋升到老生代的平均大小大于老生代的剩余大小,则会返回true,认为需要一次full gc。

通过注释也可以知道:

 

If the remaining free space in the old generation is less than
that expected to be needed by the next collection, do a full
collection now.

如果老生代的剩余空间少于下一次收集所需的剩余空间,那么现在就做一个完整的收集。

如果 padded_average_promoted_in_bytes()大于老生代剩余空间,那么就返回true,表示要触发一次fullgc。

那么padded_average_promoted_in_bytes()这个平均大小是怎么算出来的呢?我们去看看:

 

// Padded average in bytes
size_t padded_average_promoted_in_bytes() const {
  return (size_t)_avg_promoted->padded_average();
}

float padded_average() const { return _padded_avg; }

// A weighted average that includes a deviation from the average,
// some multiple of which is added to the average.
//
// This serves as our best estimate of an upper bound on a future
// unknown.
class AdaptivePaddedAverage : public AdaptiveWeightedAverage {
 private:
  float          _padded_avg;     // The last computed padded average
  float          _deviation;      // Running deviation from the average
  unsigned       _padding;        // A multiple which, added to the average,
                                  // gives us an upper bound guess.

 protected:
  void set_padded_average(float avg)  { _padded_avg = avg;  }
  void set_deviation(float dev)       { _deviation  = dev;  }

 public:
  AdaptivePaddedAverage() :
    AdaptiveWeightedAverage(0),
    _padded_avg(0.0), _deviation(0.0), _padding(0) {}

  AdaptivePaddedAverage(unsigned weight, unsigned padding) :
    AdaptiveWeightedAverage(weight),
    _padded_avg(0.0), _deviation(0.0), _padding(padding) {}

  // Placement support
  void* operator new(size_t ignored, void* p) throw() { return p; }
  // Allocator
  void* operator new(size_t size) throw() { return CHeapObj<mtGC>::operator new(size); }

  // Accessor
  float padded_average() const         { return _padded_avg; }
  float deviation()      const         { return _deviation;  }
  unsigned padding()     const         { return _padding;    }

  void clear() {
    AdaptiveWeightedAverage::clear();
    _padded_avg = 0;
    _deviation = 0;
  }

  // Override
  void  sample(float new_sample);

  // Printing
  void print_on(outputStream* st) const;
  void print() const;
};

可以从代码和注释中我们发现:

加权平均值包括与平均值的偏差,其平均值加上其中的一些倍数。 这是对未来未知数的上限的最佳估计。

也就是通过这样的算法,虚拟机估算出下次分配可能会发生无法分配的问题,于是提前预测到可能的问题,提前发生一次full gc

于是这次full gc就发生了!

那么你也许有疑问说[Full GC (Ergonomics) 的Ergonomics究竟是个什么东东?

Ergonomics翻译成中文,一般都是“人体工程学”。在JVM中的垃圾收集器中的Ergonomics就是负责自动的调解gc暂停时间和吞吐量之间的平衡,然后你的虚拟机性能更好的一种做法。

对于注重吞吐量的收集器来说,在某个generation被过渡使用之前,GC ergonomics就会启动一次GC。

正如我们前面提到的,发生本次full gc正是在使用Parallel Scavenge收集器的情况下发生的。

而Parallel Scavenge正是一款注重吞吐量的收集器:

Parallel Scavenge的目标是达到一个可控的吞吐量,吞吐量=程序运行时间/(程序运行时间+GC时间),如程序运行了99s,GC耗时1s,吞吐量=99/(99+1)=99%。Parallel Scavenge提供了两个参数用以精确控制吞吐量,分别是用以控制最大GC停顿时间的-XX:MaxGCPauseMillis及直接控制吞吐量的参数-XX:GCTimeRatio。

好,就到这里吧。

总之,以后遇到Full GC,不一定只有Allocation Failure,还有更多,比如本文中的“Ergonomics”。

 

原文地址

https://cloud.tencent.com/developer/article/1082687

</article>

与[转帖]Full GC (Ergonomics) 产生的原因相似的内容:

[转帖]Full GC (Ergonomics) 产生的原因

发生Full GC,有很多种原因,不仅仅是只有Allocation Failure。 还有以下这么多: #include "precompiled.hpp" #include "gc/shared/gcCause.hpp" const char* GCCause::to_string(GCCause

【转帖】Java Full GC (Ergonomics) 的排查

文章目录 1. Full GC (Ergonomics)1.1 Java 进程一直进行 Full GC1.2 Full GC 的原因1.3 检查堆占用 2. 代码检查3. 解决方式 1. Full GC (Ergonomics) 1.1 Java 进程一直进行 Full GC 例行检查线上运行的 J

【转帖】JAVA GC日志分析

https://zhuanlan.zhihu.com/p/613592552 ​ 目录 1. GC分类 针对HotSpot VM的实现,它里面的GC按照回收区域又分为两大种类型:一种是部分收集(Partial GC),一种是整堆收集(Full GC) 部分收集(Partial GC):不是完整收集整

[转帖]JVM 使用mat分析Dump文件排查大对象解决系统full GC问题

https://www.cnblogs.com/east7/p/16989436.html 摘要:介绍内存分析工具Mat查找大对象的使用方法,定位full GC根源,拉升系统吞吐量,避免内存泄漏。 引言 线上服务器频繁发生full GC,直接拉低系统吞吐量,甚至OOM。今天我们来一起学习一下如何利用

[转帖]JVM 使用mat分析Dump文件排查大对象解决系统full GC问题

https://www.cnblogs.com/east7/p/16989436.html 摘要:介绍内存分析工具Mat查找大对象的使用方法,定位full GC根源,拉升系统吞吐量,避免内存泄漏。 引言 线上服务器频繁发生full GC,直接拉低系统吞吐量,甚至OOM。今天我们来一起学习一下如何利用

[转帖]JVM 使用mat分析Dump文件排查大对象解决系统full GC问题

https://www.cnblogs.com/east7/p/16989436.html 摘要:介绍内存分析工具Mat查找大对象的使用方法,定位full GC根源,拉升系统吞吐量,避免内存泄漏。 引言 线上服务器频繁发生full GC,直接拉低系统吞吐量,甚至OOM。今天我们来一起学习一下如何利用

【转帖】71.常用的显示GC日志的参数、GC日志分析、日志分析工具的使用

目录 1.常用的显示GC日志的参数2.图解垃圾`GC`日志(重要)3.日志分析工具的使用 1.常用的显示GC日志的参数 解释: 日志中,GC和Full GC表示的是GC的类型。GC只在新生代进行,Full GC包括新生代和老年代、方法区。 Allocation Failure:GC发生的原因,一般新

[转帖]线上Java 高CPU占用、高内存占用排查思路

一、前言 处理过线上问题的同学基本上都会遇到系统突然运行缓慢,CPU 100%,以及Full GC次数过多的问题。当然,这些问题的最终导致的直观现象就是系统运行缓慢,并且有大量的报警。本文主要针对系统运行缓慢这一问题,提供该问题的排查思路,从而定位出问题的代码点,进而提供解决该问题的思路。 二、分析

【转帖】32.MinorGC、MajorGC和FullGC的对比

目录 1.MinorGC、MajorGC和FullGC的对比2.GC触发机制 1.MinorGC、MajorGC和FullGC的对比 1.JVM在进行GC的时候,并不是每次都是对新生代、老年代、永久代/元空间一起回收的,大部分的回收都是值新生代。 2.针对HotSpot VM的实现,它里面的GC按照

[转帖]深入分析 OpenJDK G1 FullGC 原理

https://my.oschina.net/u/6150560/blog/7928455 欢迎关注【字节跳动 SYS Tech】公众号。字节跳动 SYS Tech 聚焦系统技术领域,与大家分享前沿技术动态、技术创新与实践、行业技术热点分析等内容。 导读 本文主要从代码层面介绍 OpenJDK G1