关于gc日志中Desired Survivor的疑问和对象晋升老年代的小结
问题背景
(下面的所有内容都是根据书上的Serial/Serial Old收集器下的情况)
在《深入理解JVM》一书中的——3.6.3长期存活的对象将进入老年代的介绍中,
一个例子的jvm参数中加了这一行
-XX:+printTenuringDestribution
意思是希望每次新生代gc后,可以跟踪Survivor区中的对象的年龄分布。
然后还设置了
-XX:MaxTenuringThreshole=
这是晋升老年代的年龄阈值。
然后在gc日志中,出现了这样的字眼:
[GC [DefNew Desired Survivor size bytes, new threshold (max ) - age : bytes, total
threshold很显然就是说,设置的晋升老年代的年龄阈值为1,然后下面的age开头的那行,很明显就是在描述Survivor中对象的年龄分布。
百度后知道,age 1后面第一个字节是年龄等于这个1的所有对象的内存占用大小;然后后面那个total的bytes值是指年龄<=这个age的对象总共占用的内存大小。
然后就被这个Desired Survivor size给卡住了,这是什么呢?渴望的理想的Survivor大小???
Desired Survivor size和vm参数-XX:TargetSurvivorRatio
要讲这个Desired Survivor size就要知道一个参数:-XX:TargetSurvivorRatio
这个参数的含义是:设定survivor区的目标使用率。默认50,即survivor区对象目标使用率为50%
如果只有一个MaxTenuringThreshold,只有大于这个年龄的对象才能晋升老年代的话,肯定不足以应付更加复杂的情况,如果有很多还没到这个你设置的MaxThreshold的对象呆在Survivor区的话,这样Survivor区的内存很快就会满的。所以这个年龄阈值其实是在运行的时候会动态更改的。
首先,我们要明确,我们设置的MaxTenuringThreshold是最大的阈值,然后在运行的过程中,虚拟机会动态计算晋升的阈值。
来看看JVM中的关键源码:
uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {
//TargetSurvivorRatio默认50,意思是:在回收之后希望survivor区的占用率达到这个比例
size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/);
size_t total = ;
uint age = ;
assert(sizes[] == , "no objects with age zero should be recorded");
while (age < table_size) {//table_size=16
total += sizes[age];
//如果加上这个年龄的所有对象的大小之后,占用量>期望的大小,就设置age为新的晋升阈值
if (total > desired_survivor_size) break;
age++;
}
uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;
这里就可以看到这个Desired survivor size的计算公式了:
desired survivor size = (survivor区容量 * TargetSurvivorRatio)/100(其实就是survivor容量乘以这个targetSurvivorRatio的比值)
这个TargetSurvivorRatio就是上面介绍的那个参数设置的值,默认是50,一般很少会去改。
然后这个sizes数组是个age table,存的是个个年龄的所有对象的总大小。
所以,代码的意思是,如果<=某个age(设n岁)的对象累加起来的内存大小,大于我们的desiredSurvivorSize的话,就要看这个n值,如果这个n小于我们设的MaxThreshold,那么这次gc的阈值就是这个n,否则就是我们设的maxThreshold。
所以这也是为什么gc日志中会有new threshold 1(max 1)的字眼吧,max是我们设置的,前面那个是动态计算的吧。
晋升老年代的总结
1.担保机制
新生代中垃圾收集采用的是复制算法,当Survivor区的内存大小不足以装下一次Minor Gc中所有的存活对象的时候,就启动担保机制,将Survivor不够放的活对象,直接进入到老年代。
2.大对象直接进入老年代
虚拟机提供了个-XX:pretenureSizeThreshold参数,令内存大于这个设置值的对象直接在老年代分配。这个参数只对Serial和ParNew收集器有效,Parallel Scavenge收集器不认识这个参数,一般它也不需要设置,如果遇到必须要设置这个参数的场合,可以考虑ParNew+CMS的收集器组合。
3.长期存活的对象进入老年代
就是上文说的,在Minor gc中,把age大于设置的-XX:MaxTenuringThresholed值的对象晋升到老年代。
这个age是这样计算的,jvm为每个对象定义了一个对象年龄(Age)计数器,如果对象在Eden出生并经过第一次Minor GC后仍然存活,并能够被Survivor区容纳的话,将被移到Survivor区中,并且对象年龄设为1。
对象在Survivor区中每“熬过”一次Minor GC,年龄就加一岁。
4.动态对象年龄判断
这里要说明下一个误区。
书上是这样讲的:如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
然而我们上面分析过了:
1. 不是某个年龄的对象总和,而是<=某个年龄的对象总和。
2.也不一定是大于SurVivor空间的一半,只是默认TargetSurvivorRatio设为50才是一半,应该是根据这个参数才对。
参考文章:
https://blog.csdn.net/foolishandstupid/article/details/77596050——《jvm源码阅读笔记[2]:你不知道的晋升阈值TenuringThreshold详解》
https://blog.csdn.net/zero__007/article/details/52797684——《MaxTenuringThreshold 和 TargetSurvivorRatio参数说明》
https://blog.csdn.net/u014493323/article/details/82921740——《jvm误区--动态对象年龄判定》
关于gc日志中Desired Survivor的疑问和对象晋升老年代的小结的更多相关文章
- 实验: survivor放不下的对象进入老年代
实验一: 存活对象包含 小于survivor大小的对象 + 大于survivor的对象 private static final Integer _1MB = 1024 * 1024; /** * - ...
- Major GC 是清理老年代。 Full GC 是清理整个堆空间—包括年轻代和老年代。
Major GC 是清理老年代. Full GC 是清理整个堆空间—包括年轻代和老年代.
- 堆里面的分区:Eden,Survivor(from+ to),老年代,各自的特点
堆里面分为新生代和老生代(java8取消了永久代,采用了Metaspace),新生代包含Eden+Survivor区,survivor区里面分为from和to区,内存回收时,如果用的是复制算法,从fr ...
- 获取任意可序列化对象的Xml字符串,方便在日志中查看任一所感兴趣的对象。
代码: public static string GetLoggingString(this object obj) { using (var stream = new MemoryStream()) ...
- 记录一次JVM调优【GC日志的分析】
首先查看服务器版本默认信息: 修改tomcat/bin/catalina.sh,在最顶端加入JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -Xloggc ...
- 【转】Java中的新生代、老年代、永久代和各种GC
JVM中的堆,一般分为三大部分:新生代.老年代.永久代: 1 新生代 主要是用来存放新生的对象.一般占据堆的1/3空间.由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收. 新生代又分为 ...
- java虚拟机-GC-新生代的GC、老年代的GC
名词解释: GC:垃圾收集器 Minor GC:新生代GC,指发生在新生代的垃圾收集动作,所有的Minor GC都会触发全世界的暂停(stop-the-world),停止应用程序的线程,不过这个过程非 ...
- JVM中的堆的新生代、老年代、永久代详解
JVM中的堆一般分为三大部分:新生代.老年代.永久代,其大致的占比如下: 一.新生代 新生代主要用来存放新生的对象.一般占据堆空间的1/3.在新生代中,保存着大量的刚刚创建的对象,但是大部分的对象都 ...
- GC 老年代 新生代
参考资料: http://blog.csdn.net/flamezyg/article/details/44673951 http://www.blogjava.net/ldwblog/archive ...
随机推荐
- 人生苦短之Python多线程
#encoding=utf-8 import threading import time ''' python多线程并不是真正意义上的多线程,通常我们所说的多线程是多个线程同时执行某功能,而在pyth ...
- 为 Android 平台开发一个输入法
学习目标: 实现新的输入法 学习目的: 掌握Android输入法框架 学习收获: Android 1.5 新特色之一就是输入法框架(Input Method Framework,IMF),正是它的出现 ...
- 【转载】Myeclipse中实现js的提示
近期需要大量使用JS来开发,但是MyEclipse2014自带的JS编辑器没有代码提示的功能,开发效率有点低,所以安装了一个Spket的插件,过程非常简单,SVN插件的安装比这个更简单. Spket插 ...
- Python模块:os
OS模块常用用法: os.name() #判断当前使用的系统环境,windows则返回 ‘nt’,Linux则返回‘posix’ os.getcwd() #显示当前目录 os.listdir() #以 ...
- 【C/C++】计算两个整数的最大公约数和最小公倍数
算法一 任何>1的整数都可以写成一个或多个素数因子乘积的形式,且素数乘积因子以非递减序出现. 则整数x,y可以分别标记为:x=p1x1p2x2...pmxm y=p1y1p2y2...pmym ...
- Unity3D 开发ios时困扰多时游戏开始画面图片的分辨率
- Numbers Exchange
题意: Eugeny有n张卡片,他希望和Nikolay交换一些卡片使得他拥有的奇数数字和偶数数字的卡片数目一样,且所有数字都不同. Nikolay有m张卡片,分别写着1到m.问最少交换几次,能够满足要 ...
- 安装mosquitto报缺少dll文件的错误
解决:下载缺少的dll组件,放到安装目录. 报错:The procedure entry point CRYPTO_memcmp could not be located in the dynami ...
- TypeScript完全解读(26课时)_13.TypeScript完全解读-高级类型(2)
13.TypeScript完全解读-高级类型(2) 高级类型中文网的地址:https://typescript.bootcss.com/advanced-types.html 创建文件并在index. ...
- Flutter实战视频-移动电商-61.购物车_商品数量的加减操作
61.购物车_商品数量的加减操作 provide/cart.dart pages/cart_page/cart_count.dart 先引入provide和cartProvide 定义接收一个item ...