用法: -XX:MaxTenuringThreshold=3
该参数主要是控制新生代需要经历多少次GC晋升到老年代中的最大阈值。在JVM中用4个bit存储(放在对象头中),所以其最大值是15。
但并非意味着,对象必须要经历15次YGC才会晋升到老年代中。例如,当survivor区空间不够时,便会提前进入到老年代中,但这个次数一定不大于设置的最大阈值。
那么JVM到底是如何来计算S区对象晋升到Old区的呢?
首先介绍另一个重要的JVM参数:
-XX:TargetSurvivorRatio
:一个计算期望s区存活大小(Desired survivor size)的参数。默认值为50,即50%。
当一个S区中所有的age对象的大小如果大于等于Desired survivor size,则重新计算threshold,以age和MaxTenuringThreshold两者的最小值为准。
以一个Demo为例:
//-Xmx200M -Xmn50m -XX:TargetSurvivorRatio=60 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:MaxTenuringThreshold=3
//最小堆为50M,默认SurvivorRatio为8,那么可以知道Eden区为40M,S0和S1为5M
public class App {
public static void main(String[] args) throws InterruptedException {
// main方法作为主线程,变量不会被回收
byte[] byte1 = new byte[1 * 1024 * 1024];
byte[] byte2 = new byte[1 * 1024 * 1024];
YGC(40);
Thread.sleep(3000);
YGC(40);
Thread.sleep(3000);
YGC(40);
Thread.sleep(3000);
// 这次再ygc时, 由于byte1和byte2的年龄经过3次ygc后已经达到3(-XX:MaxTenuringThreshold=3), 所以会晋升到old
YGC(40);
// ygc后, s0(from)/s1(to)的空间为0
Thread.sleep(3000);
// 达到TargetSurvivorRatio这个比例指定的值,即 5M(S区)*60%(TargetSurvivorRatio)=3M(Desired survivor size)
byte[] byte4 = new byte[1 * 1024 * 1024];
byte[] byte5 = new byte[1 * 1024 * 1024];
byte[] byte6 = new byte[1 * 1024 * 1024];
// 这次ygc时, 由于s区已经占用达到了60%(-XX:TargetSurvivorRatio=60),
// 所以会重新计算对象晋升的min(age, MaxTenuringThreshold) = 1
YGC(40);
Thread.sleep(3000);
// 由于前一次ygc时算出age=1, 所以这一次再ygc时, byte4, byte5, byte6就要晋升到Old,
// 而不需要等MaxTenuringThreshold这么多次, 此次ygc后, s0(from)/s1(to)的空间再次为0, 对象全部晋升到old
YGC(40);
Thread.sleep(3000);
System.out.println("GC end!");
}
//塞满Eden区,局部变量会被回收,作为触发GC的小工具
private static void YGC(int edenSize){
for (int i = 0 ; i < edenSize ; i ++) {
byte[] byte1m = new byte[1 * 1024 * 1024];
}
}
}
可以看到结果
//第一次YGC
2017-07-22T17:43:50.615-0800: [GC (Allocation Failure) 2017-07-22T17:43:50.615-0800: [ParNew: 39936K->2812K(46080K), 0.0126581 secs] 39936K->2812K(125952K), 0.0127387 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
//第二次YGC
2017-07-22T17:43:53.653-0800: [GC (Allocation Failure) 2017-07-22T17:43:53.653-0800: [ParNew: 43542K->2805K(46080K), 0.0144079 secs] 43542K->2805K(125952K), 0.0144607 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
//第三次YGC
2017-07-22T17:43:56.679-0800: [GC (Allocation Failure) 2017-07-22T17:43:56.679-0800: [ParNew: 43329K->2877K(46080K), 0.0010447 secs] 43329K->2877K(125952K), 0.0010784 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
//三次YGC后,此时age达到MaxTenuringThreshold阈值3,再次YGC时,会晋升到Old区,可以看到此时新生代空间为0
2017-07-22T17:43:59.691-0800: [GC (Allocation Failure) 2017-07-22T17:43:59.691-0800: [ParNew: 43604K->0K(46080K), 0.0065182 secs] 43604K->2675K(125952K), 0.0065664 secs] [Times: user=0.01 sys=0.01, real=0.00 secs]
//分配3M不回收的对象,经历一次YGC,此时age=1
2017-07-22T17:44:02.708-0800: [GC (Allocation Failure) 2017-07-22T17:44:02.708-0800: [ParNew: 40731K->3072K(46080K), 0.0050035 secs] 43406K->5747K(125952K), 0.0050444 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
//此时可以看到,3M的对象也被晋升到了Old区,这可以证明,如果只靠MaxTenuringThreshold来决定多少次YGC才晋升到Old区的话,此时age=1,并没有达到阈值。
//因为此时3m达到TargetSurvivorRatio=60(5M*60=3m)的要求,那么前一次YGC,会重新计算对象晋升的threshold=min(age, MaxTenuringThreshold) = min(1,3) = 1次,所以此时晋升到了Old区
2017-07-22T17:44:05.722-0800: [GC (Allocation Failure) 2017-07-22T17:44:05.722-0800: [ParNew: 43806K->0K(46080K), 0.0060735 secs] 46481K->5747K(125952K), 0.0061215 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
GC end!
Heap
par new generation total 46080K, used 18003K [0x00000007b3800000, 0x00000007b6a00000, 0x00000007b6a00000)
eden space 40960K, 43% used [0x00000007b3800000, 0x00000007b4994d88, 0x00000007b6000000)
from space 5120K, 0% used [0x00000007b6000000, 0x00000007b6000000, 0x00000007b6500000)
to space 5120K, 0% used [0x00000007b6500000, 0x00000007b6500000, 0x00000007b6a00000)
concurrent mark-sweep generation total 79872K, used 5747K [0x00000007b6a00000, 0x00000007bb800000, 0x00000007c0000000)
Metaspace used 3267K, capacity 4494K, committed 4864K, reserved 1056768K
class space used 354K, capacity 386K, committed 512K, reserved 1048576K
另外你可以在JVM启动参数中加上-XX:+PrintTenuringDistribution,该参数可以输出age的额外信息。
2017-07-22T18:07:51.401-0800: [GC (Allocation Failure) 2017-07-22T18:07:51.401-0800: [ParNew
Desired survivor size 3145728 bytes, new threshold 3 (max 3)
- age 1: 2854440 bytes, 2854440 total
: 39936K->2819K(46080K), 0.0136187 secs] 39936K->2819K(125952K), 0.0138705 secs] [Times: user=0.01 sys=0.01, real=0.01 secs]
2017-07-22T18:07:54.452-0800: [GC (Allocation Failure) 2017-07-22T18:07:54.452-0800: [ParNew
Desired survivor size 3145728 bytes, new threshold 3 (max 3)
- age 2: 2682504 bytes, 2682504 total
: 43549K->2918K(46080K), 0.0172463 secs] 43549K->2918K(125952K), 0.0173506 secs] [Times: user=0.00 sys=0.00, real=0.02 secs]
2017-07-22T18:07:57.484-0800: [GC (Allocation Failure) 2017-07-22T18:07:57.484-0800: [ParNew
Desired survivor size 3145728 bytes, new threshold 3 (max 3)
- age 3: 2680152 bytes, 2680152 total
: 43442K->2723K(46080K), 0.0115410 secs] 43442K->2723K(125952K), 0.0116179 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
2017-07-22T18:08:00.511-0800: [GC (Allocation Failure) 2017-07-22T18:08:00.511-0800: [ParNew
Desired survivor size 3145728 bytes, new threshold 3 (max 3)
: 43449K->0K(46080K), 0.0226608 secs] 43449K->2673K(125952K), 0.0228187 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
2017-07-22T18:08:03.547-0800: [GC (Allocation Failure) 2017-07-22T18:08:03.547-0800: [ParNew
Desired survivor size 3145728 bytes, new threshold 1 (max 3)
- age 1: 3145776 bytes, 3145776 total
: 40731K->3072K(46080K), 0.0055686 secs] 43404K->5745K(125952K), 0.0056352 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
2017-07-22T18:08:06.572-0800: [GC (Allocation Failure) 2017-07-22T18:08:06.572-0800: [ParNew
Desired survivor size 3145728 bytes, new threshold 3 (max 3)
: 43806K->0K(46080K), 0.0068945 secs] 46479K->5745K(125952K), 0.0070480 secs] [Times: user=0.00 sys=0.01, real=0.00 secs]
GC end!
Heap
par new generation total 46080K, used 18003K [0x00000007b3800000, 0x00000007b6a00000, 0x00000007b6a00000)
eden space 40960K, 43% used [0x00000007b3800000, 0x00000007b4994d88, 0x00000007b6000000)
from space 5120K, 0% used [0x00000007b6000000, 0x00000007b6000000, 0x00000007b6500000)
to space 5120K, 0% used [0x00000007b6500000, 0x00000007b6500000, 0x00000007b6a00000)
concurrent mark-sweep generation total 79872K, used 5745K [0x00000007b6a00000, 0x00000007bb800000, 0x00000007c0000000)
Metaspace used 3273K, capacity 4494K, committed 4864K, reserved 1056768K
class space used 354K, capacity 386K, committed 512K, reserved 1048576K
网友评论