堆内存增大,GC一次能处理的数量变大,吞吐量大;但是GC一次的时间会变长,导致后面排队的线程等待时间变长;
向反,如果堆内存小,GC一次时间短,排队等待的线程等待时间变短,延迟减少,但一次请求的数量变小。
一、吞吐量与响应时间 吞吐量 = CPU在用户应用程序运行的时间 / (CPU在用户应用程序运行的时间 + CPU垃圾回收的时间) FULL GC,串行垃圾回收会使用应用停顿,响应用户时间长
二、垃圾回收器算法比较 串行回收算法:会停止当前应用进程,回收垃圾,停顿时间久,吞吐量大,响应时间长 并行回收算法: 是多个线程同时执行串行回收算法(多核),也会使应用停顿,吞吐量大,响应时间长,用户体验差 并发回收算法:应用和垃圾回收多个线程并发执行,抢占应用的CPU资源,吞吐量相对小,响应时间短,用户体验好 G1 : 并发 + 并行回收 + 标记管理
高吞吐量和低暂停时间是应用所希望的,因为运行JVM不是为了运行垃圾回收线程的,而是能带来价值的应用系统。另外,平均暂停时间可能比较小,但是最大暂停时间却可能比较大,对于交互式应用程序,最大暂停时间应该比较小。然而,高吞吐量和低暂停时间是竞争关系。
比如,为了避免GC线程潜在的导致与用户线程发生线程同步和数据不一致问题,这要求GC线程在决定哪些对象可以回收,哪些对象仍然不能回收(仍然被活下来的对象引用)时的过程中,用户线程不能修改对象的状态,基于此,用户线程在GC过程中必须停下来(更确切的说,根据所用GC算法不同,用户线程可能在一次GC的某几个阶段停下来而不是完全暂停,比如CMS垃圾收集,一次GC分6个阶段,但是只有两个阶段需要暂停用户线程)。不仅如此,线程切换也会带来额外的开销:上下文切换带来的直接开销,缓存作用带来的间接开销(causes additional costs for thread scheduling: direct costs through context switches and indirect costs because of cache effects),在加上JVM内部的安全感考量,这意味着GC不仅仅带来GC线程本身的开销,还额外的增了一些开销,这些开销对于应用来说,都是"无价值"开销。因此,为了获得最大吞吐量,JVM必须尽可能少的运行GC,只有在迫不得已的情况下(比如新生代或者老年代已经满了)才运行GC。但是,这种方式也有问题,只在不得已的情况下才运行GC,那么每次运行GC时,需要做的事情会很多,比如有更多的对象积累在堆上等待回收,每次的GC时间会很高,由此引起的平均和最大暂停时间也会很高,这就要求GC不能在迫不得已的情况才运行,这回到了刚才的问题。
当设计GC算法或者选用给予某种GC算法的垃圾收集器时,我们要根据应用的特点选择合适的收集器,每个垃圾收集器要么只是把吞吐量和暂停时间两个目标之一作为它的设计目标,要么在两个之间做一个折中而兼顾两个目标。
对于高吞吐量,在年轻态可以使用Parallel Scavenge,年老态可以使用Parallel Old垃圾收集器。
使用-XX:+UseParallelOldGC开启
可以将-XX:ParallelGCThreads根据CPU的个数进行调整。可以是CPU数的1/2或者5/8
对于低延迟的应用,在年轻态可以使用ParNew,年老态可以使用CMS垃圾收集器。
可以使用-XX:+UseConcMarkSweepGC和-XX:+UseParNewGC打开。
可以将-XX:ParallelGCThreads 根据CPU的个数进行调整。可以是CPU数的1/2或者5/8
可以调整-XX:MaxTenuringThreshold(晋升年老代年龄)调高,默认是15。这样可以减少年老代GC的压力
可以-XX:TargetSurvivorRatio,调整Survivor的占用比率。默认50%。调高可以提供Survivor区的利用率
可以调整-XX:SurvivorRatio,调整Eden和Survivor的比重。默认是8。这个比重越小,Survivor越大,对象可以在年轻态呆更多时间。
-XX:NewRatio
新生代(Eden + 2*S)与老年代(不包括永久区)的比值
4 表示新生代 :老年代 = 1:4 ,意思是老年代占 4/5
-XX:SurvivorRatio
2个Survivor区和Eden区的比值
8 表示 两个Survivor : Eden = 2: 8 ,每个Survivor占 1/10