发生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) 产生的原因的更多相关文章

  1. GC Ergonomics间接引发的锁等待超时问题排查分析

    1. 问题背景 上周线上某模块出现锁等待超时,如下图所示: 我虽然不是该模块负责人,但出于好奇,也一起帮忙排查定位问题. 这里的业务背景就是在执行到某个地方时,需要去表中插入一批数据,这批数据需要根据 ...

  2. java.lang.OutOfMemoryError GC overhead limit exceeded原因分析及解决方案

    最近一个上线运行良好的项目出现用户无法登录或者执行某个操作时,有卡顿现象.查看了日志,出现了大量的java.lang.OutOfMemoryError: GC overhead limit excee ...

  3. GC耗时高,原因竟是服务流量小?

    原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介 最近,我们系统配置了GC耗时的监控,但配置上之后,系统会偶尔出现GC耗时大于1s的报警,排查花了一些力气,故在这里 ...

  4. JVM实用参数(八)GC日志

    本系列的最后一部分是有关垃圾收集(GC)日志的JVM参数.GC日志是一个很重要的工具,它准确记录了每一次的GC的执行时间和执行结果,通过分析GC日志可以优化堆设置和GC设置,或者改进应用程序的对象分配 ...

  5. GC之一--GC 的算法分析、垃圾收集器、内存分配策略介绍

    一.概述 垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了. jvm 中,程序计数器.虚拟机栈.本 ...

  6. GC参考手册 —— GC 调优(基础篇)

    GC调优(Tuning Garbage Collection)和其他性能调优是同样的原理.初学者可能会被 200 多个 GC参数弄得一头雾水, 然后随便调整几个来试试结果,又或者修改几行代码来测试.其 ...

  7. GC参考手册 —— GC 调优(工具篇)

    JVM 在程序执行的过程中, 提供了GC行为的原生数据.那么, 我们就可以利用这些原生数据来生成各种报告.原生数据(raw data) 包括: 各个内存池的当前使用情况, 各个内存池的总容量, 每次G ...

  8. GC参考手册 —— GC 算法(实现篇)

    学习了GC算法的相关概念之后, 我们将介绍在JVM中这些算法的具体实现.首先要记住的是, 大多数JVM都需要使用两种不同的GC算法 —— 一种用来清理年轻代, 另一种用来清理老年代. 我们可以选择JV ...

  9. JVM垃圾回收(四)- GC算法:实现(1)

    GC算法:实现 上面我们介绍了GC算法中的核心概念,接下来我们看一下JVM里的具体实现.首先必须了解的一个重要的事实是:对于大部分的JVM来说,两种不同的GC算法是必须的,一个是清理Young Gen ...

  10. JDK1.7 Update14 HotSpot虚拟机GC收集器

    在测试服务器上使用如下命令可以查看当前使用的 GC收集器,当然不止这一个命令可以看到,还有其他一些方式 第三列”=”表示第四列是参数的默认值,而”:=” 表明了参数被用户或者JVM赋值了 [csii@ ...

随机推荐

  1. 这项评测,华为云GaussDB(for MySQL)顺利通过

    摘要:近日,中国信息通信研究院(简称"中国信通院")公布了第十五批"可信数据库"评测结果.华为云GaussDB(for MySQL)凭借过硬的技术实力顺利通过& ...

  2. Mac问题记录

    1. "App" can't be opened because Apple cannot check it for malicious software. 一般来说,在Syste ...

  3. Beyond Compare常用快捷键

    [会话]菜单的功能与快捷键 [文件]菜单的功能与快捷键 [编辑]菜单的功能与快捷键 [搜索]菜单的功能与快捷键

  4. DS | 折半查找二叉判定树的画法

    以下给出我在学习中总结的一种比较简便的 构造折半二叉判定树 的思路以及方法: 思路分析: 在计算 \(mid\) 值时,使用的时 \(mid=(low+high)/2\) .这里由于 \(mid\) ...

  5. AnaConda 虚拟环境创建失败的解决方案

    问题:创建环境时,AnaConda界面下放一直显示正在创建中,然后过几分钟报错! 我的解决方法:--关闭 VPN... 其他解决方案请参考这篇文章:Here

  6. 【网络爬虫学习】第一个Python爬虫程序 & 编码与解码详解 & Pythonの实现

    本节编写一个最简单的爬虫程序,作为学习 Python 爬虫前的开胃小菜. 下面使用 Python 内置的 urllib 库获取网页的 html 信息.注意,urllib 库属于 Python 的标准库 ...

  7. C++实现简单的日期正则表达式

    简单的日期正则表达式 一个简单的日期解析程序,从yyyy-mm-dd格式的日期字符串中,分别获取年月日. 先设置一个简单的正则表达式,4位数字的"年",1-2位数字的"月 ...

  8. Spring Boot Serverless 实战系列“部署篇” | Mall 应用

    导读:SpringBoot 是基于 Java Spring 框架的套件,它预装了 Spring 的一系列组件,让开发者只需要很少的配置就可以创建独立运行的应用程序.在云原生的世界里,有大量的平台可以运 ...

  9. S3C2440移植uboot之编译烧写uboot

    目录 移植环境 获取uboot 更新交叉编译工具 配置环境变量 移植环境 主 机:VMWare--ubuntu16.04 开发板:S3C2440 编译器:arm-linux-gcc-4.3.2.tgz ...

  10. C语言常用字符串操作函数整理(详细全面)

    目录 字符串相关 1.char *gets(char *s); #include<stdio.h> 2.char *fgets(char *s, intsize, FILE *stream ...