-XX:MaxTenuringThreshold

用法: -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

转载于:https://my.oschina.net/kayCz/blog/1526578

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.JVM的gc概述 gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存。java语言并不要求jvm有gc,也没有规定gc如何工作。不过常用的jvm都有gc,而且大多数gc都使用类似的算法管理内存和执行收集操作。 在充分理解了垃圾收集算法和执行过程后,才能有效的优化它的性能。有些垃圾收集专用于特殊的应用程序。比如,实时应用程序主要是为了避免垃圾收集中断,而大多数OLTP应用程序则注重整体效率。理解了应用程序的工作负荷和jvm支持的垃圾收集算法,便可以进行优化配置垃圾收集器。 垃圾收集的目的在于清除不再使用的对象。gc通过确定对象是否被活动对象引用来确定是否收集该对象。gc首先要判断该对象是否是时候可以收集。两种常用的方法是引用计数和对象引用遍历。 1.1.引用计数 引用计数存储对特定对象的所有引用数,也就是说,当应用程序创建引用以及引用超出范围时,jvm必须适当增减引用数。当某对象的引用数为0时,便可以进行垃圾收集。 1.2.对象引用遍历 早期的jvm使用引用计数,现在大多数jvm采用对象引用遍历。对象引用遍历从一组对象开始,沿着整个对象图上的每条链接,递归确定可到达(reachable)的对象。如果某对象不能从这些根对象的一个(至少一个)到达,则将它作为垃圾收集。在对象遍历阶段,gc必须记住哪些对象可以到达,以便删除不可到达的对象,这称为标记(marking)对象。 下一步,gc要删除不可到达的对象。删除时,有些gc只是简单的扫描堆栈,删除未标记的未标记的对象,并释放它们的内存以生成新的对象,这叫做清除(sweeping)。这种方法的问题在于内存会分成好多小段,而它们不足以用于新的对象,但是组合起来却很大。因此,许多gc可以重新组织内存中的对象,并进行压缩(compact),形成可利用的空间。 为此,gc需要停止其他的活动活动。这种方法意味着所有与应用程序相关的工作停止,只有gc运行。结果,在响应期间增减了许多混杂请求。另外,更复杂的gc不断增加或同时运行以减少或者清除应用程序的中断。有的gc使用单线程完成这项工作,有的则采用多线程以增加效率。 2.几种垃圾回收机制 2.1.标记-清除收集器 这种收集器首先遍历对象图并标记可到达的对象,然后扫描堆栈以寻找未标记对象并释放它们的内存。这种收集器一般使用单线程工作并停止其他操作。 2.2.标记-压缩收集器 有时也叫标记-清除-压缩收集器,与标记-清除收集器有相同的标记阶段。在第二阶段,则把标记对象复制到堆栈的新域中以便压缩堆栈。这种收集器也停止其他操作。 2.3.复制收集器 这种收集器将堆栈分为两个域,常称为半空间。每次仅使用一半的空间,jvm生成的新对象则放在另一半空间中。gc运行时,它把可到达对象复制到另一半空间,从而压缩了堆栈。这种方法适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。 2.4.增量收集器 增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾。这会造成较小的应用程序中断。 2.5.分代收集器 这种收集器把堆栈分为两个或多个域,用以存放不同寿命的对象。jvm生成的新对象一般放在其中的某个域中。过一段时间,继续存在的对象将获得使用期并转入更长寿命的域中。分代收集器对不同的域使用不同的算法以优化性能。 2.6.并发收集器 并发收集器与应用程序同时运行。这些收集器在某点上(比如压缩时)一般都不得不停止其他操作以完成特定的任务,但是因为其他应用程序可进行其他的后台操作,所以中断其他处理的实际时间大大降低。 2.7.并行收集器 并行收集器使用某种传统的算法并使用多线程并行的执行它们的工作。在多cpu机器上使用多线程技术可以显著的提高java应用程序的可扩展性。 3.Sun HotSpot 1.4.1 JVM堆大小的调整 Sun HotSpot 1.4.1使用分代收集器,它把堆分为三个主要的域:新域、旧域以及永久域。Jvm生成的所有新对象放在新域中。一旦对象经历了一定数量的垃圾收集循环后,便获得使用期并进入旧域。在永久域中jvm则存储class和method对象。就配置而言,永久域是一个独立域并且不认为是堆的一部分。 下面介绍如何控制这些域的大小。可使用-Xms和-Xmx 控制整个堆的原始大小或最大值。 下面的命令是把初始大小设置为128M: java –Xms128m –Xmx256m为控制新域的大小,可使用-XX:NewRatio设置新域在堆中所占的比例。 下面的命令把整个堆设置成128m,新域比率设置成3,即新域与旧域比例为1:3,新域为堆的1/4或32M: java –Xms128m –X
JVM参数设置详细说明、JVM 参数设置详细说明 1: heap size a: -Xmx 指定jvm的最大heap大小,如:-Xmx=2g b: -Xms 指定jvm的最小heap大小,如:-Xms=2g,高并发应用,建议和-Xmx一样,防止因为内存收缩/突然增大带来的性能影响。 c: -Xmn 指定jvm中New Generation的大小,如:-Xmn256m。这个参数很影响性能,如果你的程序需要比较多的临时内存,建议设置到512M,如果用的少,尽量降低这个数值,一般来说128/256足以使用了。 d: -XX:PermSize= 指定jvm中Perm Generation的最小值,如:-XX:PermSize=32m。这个参数需要看你的实际情况,可以通过jmap命令看看到底需要多少。 e: -XX:MaxPermSize= 指定Perm Generation的最大值,如:-XX:MaxPermSize=64m f: -Xss 指定线程桟大小,如:-Xss128k,一般来说,webx框架下的应用需要256K。如果你的程序有大规模的递归行为,请考虑设置到512K/1M。这个需要全面的测试才能知道。不过,256K已经很大了。这个参数对性能的影响比较大的。 g: -XX:NewRatio= 指定jvm中Old Generation heap size与New Generation的比例,在使用CMS GC的情况下此参数失效,如:-XX:NewRatio=2 h: -XX:SurvivorRatio= 指定New Generation中Eden Space与一个Survivor Space的heap size比例,-XX:SurvivorRatio=8,那么在总共New Generation为10m的情况下,Eden Space为8m i: -XX:MinHeapFreeRatio= 指定jvm heap在使用率小于n的情况下,heap进行收缩,Xmx==Xms的情况下无效,如:-XX:MinHeapFreeRatio=30 j: -XX:MaxHeapFreeRatio= 指定jvm heap在使用率大于n的情况下,heap 进行扩张,Xmx==Xms的情况下无效,如:-XX:MaxHeapFreeRatio=70 k: -XX:LargePageSizeInBytes= 指定Java heap的分页页面大小, 如:-XX:LargePageSizeInBytes=128m 2: garbage collector a: -XX:+UseParallelGC 指定在New Generation使用parallel collector,并行收集,暂停,app threads,同时启动多个垃圾回收thread,不能和CMS gc一起使用。系统吨吐量优先,但是会有较长长时间的app pause,后台系统任务可以使用此 gc b: -XX:ParallelGCThreads= 指定parallel collection时启动的thread个数,默认是物理processor的个数 c: -XX:+UseParallelOldGC 指定在Old Generation使用parallel collector d: -XX:+UseParNewGC 指定在New Generation使用parallel collector,是UseParallelGC的gc的升级版本,有更好的性能或者优点,可以和CMS gc一起使用 e: -XX:+CMSParallelRemarkEnabled 在使用UseParNewGC的情况下,尽量减少mark的时间 f: -XX:+UseConcMarkSweepGC 指定在Old Generation使用concurrent cmark sweep gc、gc thread和app thread并行(在init-mark和remark时pause app thread)。app pause时间较短,适合交互性强的系统,如web server g: -XX:+UseCMSCompactAtFullCollection 在使用concurrent gc的情况下,防止memory fragmention,对live object进行整理,使memory 碎片减少 h: -XX:CMSInitiatingOccupancyFraction= 指示在old generation 在使用了n%的比例后,启动concurrent collector,默认值是68,如:-XX:CMSInitiatingOccupancyFraction=70 有个bug,在低版本(1.5.09 and early)的jvm上出现, http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6486089 i: -XX:+UseCMSInitiatingOccupancyOnly 指示只有在old generation在使用了初始化的比例后concurrent collector启动收集 3:others a: -XX:MaxTenuringThreshold= 指定一个object在经历了n次young gc后转移到old generation区,在linux64的java6下默认值是15,此参数对于throughput collector无效,如:-XX:MaxTenuringThreshold=31 b: -XX:+DisableExplicitGC 禁止java程序中的full gc,如System.gc()的调用。最好加上么,防止程序在代码里误用了。对性能造成冲击。 c: -XX:+UseFastAccessorMethods get、set方法转成本地代码 d: -XX:+PrintGCDetails 打应垃圾收集的情况如: [GC 15610.466: [ParNew: 229689K->20221K(235968K), 0.0194460 secs] 1159829K->953935K(2070976K), 0.0196420 secs] e: -XX:+PrintGCTimeStamps 打应垃圾收集的时间情况,如: [Times: user=0.09 sys=0.00, real=0.02 secs] f: -XX:+PrintGCApplicationStoppedTime 打应垃圾收集时,系统的停顿时间,如: Total time for which application threads were stopped: 0.0225920 seconds 4: a web server product sample and process JAVA_OPTS=" -server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 " 最初的时候我们用UseParallelGC和UseParallelOldGC,heap开了3G,NewRatio设成1。这样的配置下young gc发生频率约12、3秒一次,平均每次花费80ms左右,full gc发生的频率极低,每次消耗1s左右。从所有gc消耗系统时间看,系统使用率还是满高的,但是不论是young gc还是old gc,application thread pause的时间比较长,不合适 web 应用。我们也调小New Generation的,但是这样会使full gc时间加长。 后来我们就用CMS gc(-XX:+UseConcMarkSweepGC),当时的总heap还是3g,新生代1.5g后,观察不是很理想,改为jvm heap为2g新生代设置-Xmn1g,在这样的情况下young gc发生的频率变成7、8秒一次,平均每次时间40-50毫秒左右,CMS gc很少发生,每次时间在init-mark和remark(two steps stop all app thread)总共平均花费80-90ms左右。 在这里我们曾经New Generation调大到1400m,总共2g的jvm heap,平均每次ygc花费时间60-70ms左右,CMS gc的init-mark和remark之和平均在50ms左右,这里我们意识到错误的方向,或者说CMS的作用,所以进行了修改。 最后我们调小New Generation为256m,young gc 2、3秒发生一次,平均停顿时间在25毫秒左右,CMS gc的init-mark和remark之和平均在50ms左右,这样使系统比较平滑,经压力测试,这个配置下系统性能是比较高的。 在使用CMS gc的时候他有两种触发gc的方式:gc估算触发和heap占用触发。我们的1.5.0.09 环境下有次old 区heap占用在30%左右,她就频繁gc,个人感觉系统估算触发这种方式不靠谱,还是用 heap 使用比率触发比较稳妥。 这些数据都来自64位测试机,过程中的数据都是我在jboss log找的,当时没有记下来,可能存在一点点偏差,但不会很大,基本过程就是这样。 5: 总结 web server作为交互性要求较高的应用,我们应该使用Parallel+CMS,UseParNewGC这个在jdk6 -server上是默认的new generation gc,新生代不能太大,这样每次pause会短一些。CMS mark-sweep generation可以大一些,可以根据pause time实际情况控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值