JVM实用参数(六) 吞吐量收集器

JVM实用参数(六) 吞吐量收集器

原文链接 本文连接 译者:张军  校对:梁海舰

在实践中我们发现对于大多数的应用领域,评估一个垃圾收集(GC)算法如何根据如下两个标准:

  1. 吞吐量越高算法越好
  2. 暂停时间越短算法越好

首先让我们来明确垃圾收集(GC)中的两个术语:吞吐量(throughput)和暂停时间(pause times)。 JVM在专门的线程(GC threads)中执行GC。 只要GC线程是活动的,它们将与应用程序线程(application threads)争用当前可用CPU的时钟周期。 简单点来说,吞吐量是指应用程序线程用时占程序总用时的比例。 例如,吞吐量99/100意味着100秒的程序执行时间应用程序线程运行了99秒, 而在这一时间段内GC线程只运行了1秒。

术语”暂停时间”是指一个时间段内应用程序线程让与GC线程执行而完全暂停。 例如,GC期间100毫秒的暂停时间意味着在这100毫秒期间内没有应用程序线程是活动的。 如果说一个正在运行的应用程序有100毫秒的“平均暂停时间”,那么就是说该应用程序所有的暂停时间平均长度为100毫秒。 同样,100毫秒的“最大暂停时间”是指该应用程序所有的暂停时间最大不超过100毫秒。

吞吐量 VS 暂停时间

高吞吐量最好因为这会让应用程序的最终用户感觉只有应用程序线程在做“生产性”工作。 直觉上,吞吐量越高程序运行越快。 低暂停时间最好因为从最终用户的角度来看不管是GC还是其他原因导致一个应用被挂起始终是不好的。 这取决于应用程序的类型,有时候甚至短暂的200毫秒暂停都可能打断终端用户体验。 因此,具有低的最大暂停时间是非常重要的,特别是对于一个交互式应用程序。

不幸的是”高吞吐量”和”低暂停时间”是一对相互竞争的目标(矛盾)。这样想想看,为了清晰起见简化一下:GC需要一定的前提条件以便安全地运行。 例如,必须保证应用程序线程在GC线程试图确定哪些对象仍然被引用和哪些没有被引用的时候不修改对象的状态。 为此,应用程序在GC期间必须停止(或者仅在GC的特定阶段,这取决于所使用的算法)。 然而这会增加额外的线程调度开销:直接开销是上下文切换,间接开销是因为缓存的影响。 加上JVM内部安全措施的开销,这意味着GC及随之而来的不可忽略的开销,将增加GC线程执行实际工作的时间。 因此我们可以通过尽可能少运行GC来最大化吞吐量,例如,只有在不可避免的时候进行GC,来节省所有与它相关的开销。

然而,仅仅偶尔运行GC意味着每当GC运行时将有许多工作要做,因为在此期间积累在堆中的对象数量很高。 单个GC需要花更多时间来完成, 从而导致更高的平均和最大暂停时间。 因此,考虑到低暂停时间,最好频繁地运行GC以便更快速地完成。 这反过来又增加了开销并导致吞吐量下降,我们又回到了起点。
综上所述,在设计(或使用)GC算法时??,我们必须确定我们的目标:一个GC算法??只可能针对两个目标之一(即只专注于最大吞吐量或最小暂停时间),或尝试找到一个二者的折衷。

HotSpot虚拟机上的垃圾收集

该系列的第五部分我们已经讨论过年轻代的垃圾收集器。 对于年老代,HotSpot虚拟机提供两类垃圾收集算法(除了新的G1垃圾收集算法),第一类算法试图最大限度地提高吞吐量,而第二类算法试图最小化暂停时间。 今天我们的重点是第一类,”面向吞吐量”的垃圾收集算法。
我们希望把重点放在JVM配置参数上,所以我只会简要概述HotSpot提供的面向吞吐量(throughput-oriented)垃圾收集算法。 当年老代中由于缺乏空间导致对象分配失败时会触发垃圾收集器(事实上,”分配”的通常是指从年轻代提升到年老代的对象)。 从所谓的”GC根”(GC roots)开始,搜索堆中的可达对象并将其标记为活着的,之后,垃圾收集器将活着的对象移到年老代的一块无碎片(non-fragmented)内存块中,并标记剩余的内存空间是空闲的。 也就是说,我们不像复制策略那样移到一个不同的堆区域,像年轻代垃圾收集算法所做的那样。 相反地,我们把所有的对象放在一个堆区域中,从而对该堆区域进行碎片整理。 垃圾收集器使用一个或多个线程来执行垃圾收集。 当使用多个线程时,算法的不同步骤被分解,使得每个收集线程大多时候工作在自己的区域而不干扰其他线程。 在垃圾收集期间,所有的应用程序线程暂停,只有垃圾收集完成之后才会重新开始。 现在让我们来看看跟面向吞吐量垃圾收集算法有关的重要JVM配置参数。

-XX:+UseSerialGC

我们使用该标志来激活串行垃圾收集器,例如单线程面向吞吐量垃圾收集器。 无论年轻代还是年老代都将只有一个线程执行垃圾收集。 该标志被推荐用于只有单个可用处理器核心的JVM。 在这种情况下,使用多个垃圾收集线程甚至会适得其反,因为这些线程将争用CPU资源,造成同步开销,却从未真正并行运行。

-XX:+UseParallelGC

有了这个标志,我们告诉JVM使用多线程并行执行年轻代垃圾收集。 在我看来,Java 6中不应该使用该标志因为-XX:+UseParallelOldGC显然更合适。 需要注意的是Java 7中该情况改变了一点(详见本概述),就是-XX:+UseParallelGC能达到-XX:+UseParallelOldGC一样的效果。

-XX:+UseParallelOldGC

该标志的命名有点不巧,因为”老”听起来像”过时”。 然而,”老”实际上是指年老代,这也解释了为什么-XX:+UseParallelOldGC要优于-XX:+UseParallelGC:除了激活年轻代并行垃圾收集,也激活了年老代并行垃圾收集。 当期望高吞吐量,并且JVM有两个或更多可用处理器核心时,我建议使用该标志。
作为旁注,HotSpot的并行面向吞吐量垃圾收集算法通常称为”吞吐量收集器”,因为它们旨在通过并行执行来提高吞吐量。

-XX:ParallelGCThreads

通过-XX:ParallelGCThreads=<value>我们可以指定并行垃圾收集的线程数量。 例如,-XX:ParallelGCThreads=6表示每次并行垃圾收集将有6个线程执行。 如果不明确设置该标志,虚拟机将使用基于可用(虚拟)处理器数量计算的默认值。 决定因素是由Java Runtime。availableProcessors()方法的返回值N,如果N<=8,并行垃圾收集器将使用N个垃圾收集线程,如果N>8个可用处理器,垃圾收集线程数量应为3+5N/8。
当JVM独占地使用系统和处理器时使用默认设置更有意义。 但是,如果有多个JVM(或其他耗CPU的系统)在同一台机器上运行,我们应该使用-XX:ParallelGCThreads来减少垃圾收集线程数到一个适当的值。 例如,如果4个以服务器方式运行的JVM同时跑在在一个具有16核处理器的机器上,设置-XX:ParallelGCThreads=4是明智的,它能使不同JVM的垃圾收集器不会相互干扰。

-XX:-UseAdaptiveSizePolicy

吞吐量垃圾收集器提供了一个有趣的(但常见,至少在现代JVM上)机制以提高垃圾收集配置的用户友好性。 这种机制被看做是HotSpot在Java 5中引入的”人体工程学”概念的一部分。 通过人体工程学,垃圾收集器能将堆大小动态变动像GC设置一样应用到不同的堆区域,只要有证据表明这些变动将能提高GC性能。 “提高GC性能”的确切含义可以由用户通过-XX:GCTimeRatio和-XX:MaxGCPauseMillis(见下文)标记来指定。
重要的是要知道人体工程学是默认激活的。 这很好,因为自适应行为是JVM最大优势之一。 不过,有时我们需要非常清楚对于特定应用什么样的设置是最合适的,在这些情况下,我们可能不希望JVM混乱我们的设置。 每当我们发现处于这种情况时,我们可以考虑通过-XX:-UseAdaptiveSizePolicy停用一些人体工程学。

-XX:GCTimeRatio

通过-XX:GCTimeRatio=<value>我们告诉JVM吞吐量要达到的目标值。 更准确地说,-XX:GCTimeRatio=N指定目标应用程序线程的执行时间(与总的程序执行时间)达到N/(N+1)的目标比值。 例如,通过-XX:GCTimeRatio=9我们要求应用程序线程在整个执行时间中至少9/10是活动的(因此,GC线程占用其余1/10)。 基于运行时的测量,JVM将会尝试修改堆和GC设置以期达到目标吞吐量。 -XX:GCTimeRatio的默认值是99,也就是说,应用程序线程应该运行至少99%的总执行时间。

-XX:MaxGCPauseMillis

通过-XX:GCTimeRatio=<value>告诉JVM最大暂停时间的目标值(以毫秒为单位)。 在运行时,吞吐量收集器计算在暂停期间观察到的统计数据(加权平均和标准偏差)。 如果统计表明正在经历的暂停其时间存在超过目标值的风险时,JVM会修改堆和GC设置以降低它们。 需要注意的是,年轻代和年老代垃圾收集的统计数据是分开计算的,还要注意,默认情况下,最大暂停时间没有被设置。
如果最大暂停时间和最小吞吐量同时设置了目标值,实现最大暂停时间目标具有更高的优先级。 当然,无法保证JVM将一定能达到任一目标,即使它会努力去做。 最后,一切都取决于手头应用程序的行为。
当设置最大暂停时间目标时,我们应注意不要选择太小的值。 正如我们现在所知道的,为了保持低暂停时间,JVM需要增加GC次数,那样可能会严重影响可达到的吞吐量。 这就是为什么对于要求低暂停时间作为主要目标的应用程序(大多数是Web应用程序),我会建议不要使用吞吐量收集器,而是选择CMS收集器。 CMS收集器是本系列下一部分的主题。

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: JVM实用参数(六) 吞吐量收集器

原文地址:https://www.cnblogs.com/snooper/p/8717789.html

时间: 2024-08-02 07:58:32

JVM实用参数(六) 吞吐量收集器的相关文章

JVM实用参数——新生代垃圾回收

JVM实用参数目录 JVM实用参数——新生代垃圾回收 概述 第1部分  新生代垃圾回收介绍 第2部分 参数介绍 参考 第1部分  新生代垃圾回收介绍 本部分,我们将关注堆(heap) 中一个主要区域,新生代(young generation).首先我们会讨论为什么调整新生代的参数会对应用的性能如此重要,接着我们将学习新生代相关的JVM参数. 单纯从JVM的功能考虑,并不需要新生代,完全可以针对整个堆进行操作.新生代存在的唯一理由是优化垃圾回收(GC)的性能.更具体说,把堆划分为新生代和老年代有2

JVM实用参数(五)新生代垃圾回收

JVM实用参数(五)新生代垃圾回收 原文链接  作者: PATRICK PESCHLOW :译者:严亮 本部分,我们将关注堆(heap) 中一个主要区域,新生代(young generation).首先我们会讨论为什么调整新生代的参数会对应用的性能如此重要,接着我们将学习新生代相关的JVM参数. 单纯从JVM的功能考虑,并不需要新生代,完全可以针对整个堆进行操作.新生代存在的唯一理由是优化垃圾回收(GC)的性能.更具体说,把堆划分为新生代和老年代有2个好处:简化了新对象的分配(只在新生代分配内存

深入JVM虚拟机(四) Java GC收集器

深入JVM虚拟机(四) Java GC收集器 1 GC收集器 1.1 Serial串行收集器 串行收集器主要有两个特点:第一,它仅仅使用单线程进行垃圾回收:第二,它独占式的垃圾回收. 在串行收集器进行垃圾回收时,Java 应用程序中的线程都需要暂停("StopThe World"),等待垃圾回收的完成,这样给用户体验造成较差效果.虽然如此,串行收集器却是一个成熟.经过长时间生产环境考验的极为高效的收集器.新生代串行处理器使用复制算法,实现相对简单,逻辑处理特别高效,且没有线程切换的开销

JVM实用参数(一)JVM类型以及编译器模式

JVM实用参数(一)JVM类型以及编译器模式 原文地址:https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-1-jvm-types-and-compiler-modes/ 译者:赵峰,iDestiny    校对:郭蕾 现在的JVM运行Java程序(和其它的兼容性语言)时在高效性和稳定性方面做的非常出色.自适应内存管理.垃圾收集.及时编译.动态类加载.锁优化——这里仅仅列举了某些场景下会发生的神奇的事情,但他们几乎不会直接与普

JVM实用参数(四)内存调优

JVM实用参数(四)内存调优 原文地址,译文地址,作者:PATRICK PESCHLOW,译者:郑旭东  校对:梁海舰 理想的情况下,一个Java程序使用JVM的默认设置也可以运行得很好,所以一般来说,没有必要设置任何JVM参数.然而,由于一些性能问题(很不幸的是,这些问题经常出现),一些相关的JVM参数知识会是我们工作中得好伙伴.在这篇文章中,我们将介绍一些关于JVM内存管理的参数.知道并理解这些参数,将对开发者和运维人员很有帮助. 所有已制定的HotSpot内存管理和垃圾回收算法都基于一个相

JVM实用参数(三)打印所有XX参数及值

JVM实用参数(三)打印所有XX参数及值 原文地址:https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-3-printing-all-xx-flags-and-their-values/ 译者:李洪柱     校对:方腾飞 本篇文章基于Java 6(update 21oder 21之后)版本, HotSpot JVM 提供给了两个新的参数,在JVM启动后,在命令行中可以输出所有XX参数和值. -XX:+PrintFlagsFi

JVM学习记录3--垃圾收集器

贴个图 Serial收集器 最简单的收集器,单线程,收集器会暂停用户线程,称为"stop the world". ParNew收集器 Serial收集器的多线程版本,其它类似.默认线程数为CPU线程数,通过-XX:ParallelGCThreads=? 可以指定线程数 Parallel Scavenge收集器 复制算法,多线程收集器.与ParNew的区别在于,该收集器关注系统吞吐量(吞吐量=用户运行时间/(用户运行时间+垃圾回收时间)).通过-XX:MaxGCPauseMillis 设

深入理解JVM虚拟机10:JVM常用参数以及调优实践

微信公众号[Java技术江湖]一位阿里 Java 工程师的技术小站.作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux.网络.多线程,偶尔讲点Docker.ELK,同时也分享技术干货和学习经验,致力于Java全栈开发!(关注公众号后回复”Java“即可领取 Java基础.进阶.项目和架构师等免费学习资料,更有数据库.分布式.微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Java学习指南.Java程序员面试指

【JVM】jvm启动参数

-server -Xmx2048m -Xms1500m -Xmn1024m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInit