GC悲观策略之Parallel GC篇

先来看段代码:
[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"

时间: 2024-12-30 07:08:11

GC悲观策略之Parallel GC篇的相关文章

GC悲观策略之Serial GC篇

仍然是 这篇blog:GC悲观策略之Parallel GC篇中的代码,换成-Xms30m -Xmx30m -Xmn10m -XX:+UseSerialGC后执行的结果为YGC.YGC.YGC.YGC.FGC. 原因就在于Serial GC的悲观策略是不同的,Serial GC在执行YGC时,首先进入如下代码片段进行检查: [c] void DefNewGeneration::collect(bool full, bool clear_all_soft_refs, size_t size, boo

HBase GC的前生今世 – 身世篇

在之前的HBase BlockCache系列文章中已经简单提到:使用LRUBlockCache缓存机制会因为CMS GC策略导致内存碎片过多,从而可能引发臭名昭著的Full GC,触发可怕的'stop-the-world'暂停,严重影响上层业务:而Bucket Cache缓存机制因为在初始化的时候就申请了一片固定大小的内存作为缓存,缓存淘汰不再由 JVM管理,数据Block的缓存操作只是对这篇空间的访问和覆盖,因而大大减少了内存碎片的出现,降低了Full GC发生的频率.那CMS GC策略如何导

HBase GC的前生今世 – 演进篇

最原始的HBase CMS GC相当严重,经常会因为碎片过多导致Promotion Failure,严重影响业务的读写请求.幸运的是,HBase并没有止步不前,很多优化方案相继被提出并贡献给社区,本文要介绍的就是几个比较重要的核心优化,分别是针对Memstore所作的两个优化:Thread-Local Allocation Buffer和MemStore Chunk Pool 以及针对BlockCache所作的优化:BucketCache方案.在详细介绍这几个优化之前有必要简单介绍一下HBase

浅谈建站策略:域名空间篇

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 一.域名篇 1.分开多个复合地关键字,如 www.search-engine-downzj.com 而不是 www.searchengine downzj.com,这样会使到搜索引擎更容易地正确理解你地关键字; 2.要记住域名地字符数不能超过55字,因为有些搜索引擎会对此有限制; 3.在一些目录里,如yahoo,dmoz中,编缉员不会在你页面

JVM 分代GC策略分析

我们以Sun HotSpot VM来进行分析,首先应该知道,如果我们没有指定任何GC策略的时候,JVM默认使用的GC策略.Java虚拟机是按照分代的方式来回收垃圾空间,我们应该知道,垃圾回收主要是针对堆(Heap)内存进行分代回收,将对内存可以分成新生代(Young Generation).年老代(Tenured Generation)和永久代(Permanent Generation)三个部分. 分代GC 分代GC包括如下三代: 新生代(Young Generation) 新生代有划分为Ede

触发JVM进行Full GC的情况及应对策略

堆内存划分为 Eden.Survivor 和 Tenured/Old 空间,如下图所示: 从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC,对老年代GC称为Major GC,而Full GC是对整个堆来说的,在最近几个版本的JDK里默认包括了对永生带即方法区的回收(JDK8中无永生带了),出现Full GC的时候经常伴随至少一次的Minor GC,但非绝对的.Major GC的速度一般会比Minor GC慢10倍以上.下边看看有那种情况触发JVM进行Fu

jvm系列(五):Java GC 分析

Java GC就是JVM记录仪,书画了JVM各个分区的表演. 什么是 Java GC Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢.这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制.概括地说,该机制对JVM(Java Virtual Machine)中的内存进行标记,并确定哪些内存需要回收,根据一定

JVM 基础知识(GC)

几年前写过一篇关于JVM调优的文章,前段时间拿出来看了看,又添加了一些东西.突然发现,基础真的很重要.学习的过程是一个由表及里,再由里及表的过程,所谓的"温故而知新".而真正能走完这个轮回的人,也就能称为大牛或专家了.这个过程可能来来回回,这就是所谓"螺旋上升",而每一次轮回都有新的发现.   这回添加的东西主要集中在基础的一些问题上,还有一些这两年思考的问题.这些问题可能平时我们不会刻意去想,但是真正看清楚了,却发现还是大有裨益的,希望对大家都有帮助~ 一.基础概

jvm系列(十):如何优化Java GC「译」

本文由CrowHawk翻译,地址:如何优化Java GC「译」,是Java GC调优的经典佳作. Sangmin Lee发表在Cubrid上的"Become a Java GC Expert"系列文章的第三篇<How to Tune Java Garbage Collection>,本文的作者是韩国人,写在JDK 1.8发布之前,虽然有些地方有些许过时,但整体内容还是非常有价值的.译者此前也看到有人翻译了本文,发现其中有许多错漏生硬和语焉不详之处,因此决定自己翻译一份,供大