博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
GC悲观策略之Parallel GC篇
阅读量:6356 次
发布时间:2019-06-23

本文共 2796 字,大约阅读时间需要 9 分钟。

先来看段代码:

[java]
import java.util.;
public class SummaryCase{
public static void main(String[] args) throws Exception{
List caches=new ArrayList();
for(int i=0;i<7;i++){
caches.add(new byte[1024
10243]);
}
caches.clear();
for(int i=0;i<2;i++){
caches.add(new byte[1024
10243]);
}
}
}
[/java]
当用-Xms30m -Xmx30m -Xmn10m -XX:+UseParallelGC执行上面的代码时会执行几次Minor GC和几次Full GC呢?
按照eden空间不足时触发minor gc的规则,上面代码执行后的GC应为:M、M、M、M,但实际上上面代码执行后GC则为:M、M、M、F、F。
这里的原因就在于Parallel Scavenge GC时的悲观策略,当在eden上分配内存失败时且对象的大小尚不需要直接在old上分配时,会触发YGC,代码片段如下:
[java]
void PSScavenge::invoke(){
bool scavenge_was_done = PSScavenge::invoke_no_policy();
PSGCAdaptivePolicyCounters
 counters = heap->gc_policy_counters();
if (UsePerfData)
counters->update_full_follows_scavenge(0);
if (!scavenge_was_done ||
policy->should_full_GC(heap->old_gen()->free_in_bytes())) {
if (UsePerfData)
counters->update_full_follows_scavenge(full_follows_scavenge);

GCCauseSetter gccs(heap, GCCause::_adaptive_size_policy);

if (UseParallelOldGC) {
PSParallelCompact::invoke_no_policy(false);
} else {
PSMarkSweep::invoke_no_policy(false);
}
}
}
PSScavenge::invoke_no_policy{
if (!should_attempt_scavenge()) {
return false;
}
}
bool PSScavenge::should_attempt_scavenge() {
PSAdaptiveSizePolicy* policy = heap->size_policy();

size_t avg_promoted = (size_t) policy->padded_average_promoted_in_bytes();

size_t promotion_estimate = MIN2(avg_promoted, young_gen->used_in_bytes());
bool result = promotion_estimate < old_gen->free_in_bytes();

return result;
}
[/java]
在上面should_attempt_scavenge代码片段中,可以看到会比较之前YGC晋升到Old中的平均大小与当前新生代中已被使用的字节数大小,取更小的值与旧生代目前剩余空间大小对比,如更大,则返回false,就终止了YGC的执行了,当返回false 时,PSScavenge::invoke就将触发Full GC了。
在PSScavenge:invoke中还有一个条件为:policy->should_full_GC(heap->old_gen()->free_in_bytes(),来看看这段代码片段:
[java]
bool PSAdaptiveSizePolicy::should_full_GC(size_t old_free_in_bytes) {
bool result = padded_average_promoted_in_bytes() > (float) old_free_in_bytes;
return result;
}
[/java]
可看到,这段代码检查的也是之前YGC时晋升到old的平均大小是否大于了旧生代的剩余空间,如大于,则触发full gc。
总结上面分析的策略,可以看到采用Parallel GC的情况下,当YGC触发时,会有两个检查:
1、在YGC执行前,min(目前新生代已使用的大小,之前平均晋升到old的大小中的较小值) > 旧生代剩余空间大小 ? 不执行YGC,直接执行Full GC : 执行YGC;
2、在YGC执行后,平均晋升到old的大小 > 旧生代剩余空间大小 ? 触发Full GC : 什么都不做。

按照这样的说明,再来看看上面代码的执行过程中eden和old大小的变化状况:

代码 eden old YGC FGC
第一次循环 3 0 0 0
第二次循环 6 0 0 0
第三次循环 3 6 1 0
第四次循环 6 6 1 0
第五次循环 3 12 2 0
第六次循环 6 12 2 0
第七次循环 3 18 3 1
第八次循环 6 18 3 1
第九次循环 3 3 3 2
在第7次循环时,YGC后旧生代剩余空间为2m,而之前平均晋级到old的对象大小为6m,因此在YGC后会触发一次FGC。
而第9次循环时,在YGC执行前,此时新生代已使用的大小为6m,之前晋级到old的平均大小为6m,这两者去最小值为6m,这个值已大于old的剩余空间,因此就不执行YGC,直接执行FGC了。

Sun JDK之所以要有悲观策略,我猜想理由是程序最终是会以一个较为稳态的状况执行的,此时每次YGC后晋升到old的对象大小应该是差不多的,在YGC时做好检查,避免等YGC后晋升到Old的对象导致old空间不足,因此还不如干脆就直接执行FGC,正因为悲观策略的存在,大家有些时候可能会看到old空间没满但full gc执行的状况。

埋个伏笔,大家将上面的执行参数换为-XX:+UseSerialGC执行看看,会发生什么呢? :)

本文来源于"阿里中间件团队播客",原文发表时间"2010-11-07"

转载地址:http://yizma.baihongyu.com/

你可能感兴趣的文章
pm2-webshell —— 基于浏览器的终端控制台
查看>>
Mysql基准测试
查看>>
Session 撰改演示
查看>>
【转】python3 发邮件实例(包括:文本、html、图片、附件、SSL、群邮件)
查看>>
事务隔离级别(图文详解)
查看>>
canvas系列教程08-canvas各种坑
查看>>
浅析package.json中的devdependencies 和 dependencies
查看>>
又一个 iOS 侧边栏组件: SideMenu
查看>>
vue.js 打包遇到的问题
查看>>
【译】更优秀的GraphQL官方中文文档-客户端如何使用
查看>>
git pull遇到的问题
查看>>
eclipse下maven spring项目环境配置
查看>>
无缝轮播
查看>>
CTS失败项分析(2)android.telephony.cts.VisualVoicemailServiceTest#testFilter_data
查看>>
三分钟,轻松了解Dapp
查看>>
GMQ交易平台满足不同客户群体的多种投资需求
查看>>
大数据开发如何入门你必须知道这些
查看>>
关于js(es5)如何优雅地创建对象
查看>>
阿里云前端周刊 - 第 28 期
查看>>
iOS 主队列同步造成死锁的原因
查看>>