`
lewis122
  • 浏览: 237725 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

GC悲观策略之Parallel GC篇

    博客分类:
  • JVM
阅读更多

先来看段代码:

import java.util.*; 
public class SummaryCase{
	public static void main(String[] args) throws Exception{    
		List<Object> caches=new ArrayList<Object>();    
		for(int i=0;i<7;i++){    
			caches.add(new byte[1024*1024*3]);    
		}    
		caches.clear();    
		for(int i=0;i<2;i++){       
	  	caches.add(new byte[1024*1024*3]);    
		}    
		Thread.sleep(10000);    
	} 
}

当用-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,代码片段如下:

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;
}

在上面should_attempt_scavenge代码片段中,可以看到会比较之前YGC晋升到Old中的平均大小与当前新生代中已被使用的字节数大小,取更小的值与旧生代目前剩余空间大小对比,如更大,则返回false,就终止了YGC的执行了,当返回false时,PSScavenge::invoke就将触发Full GC了。
在PSScavenge:invoke中还有一个条件为:policy->should_full_GC(heap->old_gen()->free_in_bytes(),来看看这段代码片段:

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;
}

可看到,这段代码检查的也是之前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执行的状况。

 

分享到:
评论

相关推荐

    Java后端面试问题整理.docx

    常见的垃圾收集器包括Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS和G1。G1和CMS都是并行和低暂停时间的垃圾收集器,但G1引入了Region概念,可以预测停顿时间。 #### 类加载机制 JVM类加载...

    【美团】Java 岗 154 道面试题1

    39. **Java 内存分配与回收策略**:新生代和老年代划分, Minor GC 和 Major GC 分别处理年轻代和老年代的垃圾。 40. **JVM 永久代回收**:Java 8之后,永久代被元空间(Metaspace)取代,元空间在本地内存中,同样...

    山东大学软件学院NOSQL课程拓展知识,TIDB学习总结

    - GC(Garbage Collection)机制,清理旧版本的数据,保持数据的一致性。 【TiKV详解】 - 使用RocksDB存储数据,支持写入和查询操作,以及Column Families以优化存储。 - 分布式事务通过MVCC实现,保证多版本并发...

    郑州数字马力面试(后台java)经验

    - **年轻代**:使用复制算法,如Serial、ParNew、Parallel Scavenge等。 - **老年代**:使用标记-整理算法,如CMS、G1等。 **10. 年轻代使用复制算法的原因** 年轻代的对象生命周期较短,大部分对象很快会被回收,...

    面试资料(主要是java方向)

    - 垃圾回收机制:GC算法、垃圾收集器(如Serial、Parallel、CMS、G1)以及调优策略。 - 内存溢出与内存泄漏:识别和解决这些问题的方法。 6. **多线程**: - 线程的创建与状态:run()与start()的区别,线程生命...

    java面试评价表

    - **GC机制与原理**:深入理解垃圾回收机制的工作原理,包括不同类型的GC(年轻代GC、老年代GC)触发条件。 - **ClassLoader**:讲解JVM中的类加载机制,包括自定义类加载器的实现。 - **双亲委派模型**:解释双亲...

    java必读书籍

    - **乐观锁与悲观锁:** 锁的不同策略。 - **数据库相关锁机制:** 如行锁、表锁等。 - **分布式锁:** 分布式环境下的锁机制。 - **monitor:** Java对象的内部锁。 - **锁优化:** 如锁消除、锁粗化等。 - *...

    三年经验京东Java社招面经分享.pdf

    - 线程池的主要参数包括:核心线程数、最大线程数、存活时间、任务队列等,其中任务队列的类型也是决定线程池行为的重要因素之一。 2. **集合框架**: - `HashMap` 是基于哈希表的 Map 接口的非同步实现,允许...

    Java后端面试题大纲(涵盖了百分之九十的考点).pdf

    - 面向对象编程(OOP)是Java的核心概念之一,它主要包括四个特性:封装、继承、多态和抽象。 - **封装**:将数据和操作数据的方法封装在一个对象中,隐藏对象的实现细节,只暴露必要的接口。 - **继承**:允许一个...

    java核心知识点整理.pdf

    24 MinorGC 的过程(复制-&gt;清空-&gt;互换) ....................................................................................... 24 1:eden、servicorFrom 复制到 ServicorTo,年龄+1.............................

    JAVA核心知识点整理(有效)

    MinorGC 的过程(复制-&gt;清空-&gt;互换) ....................................................................................... 24 1:eden、servicorFrom 复制到 ServicorTo,年龄+1.................................

Global site tag (gtag.js) - Google Analytics