`
ganlv
  • 浏览: 34674 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

一步步优化JVM六:优化吞吐量

 
阅读更多

   如果你已经进行完了前面的步骤了,那么你应该知道这是最后一步了。在这一步里面,你需要测试应用的吞吐量和为了更高的吞吐量而优化JVM。


   这一步的输入就是应用的吞吐量性能要求。应用的吞吐量是在应用层面衡量而不是在JVM层面衡量,因此,应用必须要报告出一些吞吐量指标或者应用的某些操作的吞吐量性能指标。观察到的吞吐量指标然后用可以用来和应用需要的性能指标进行比较,如果达到或者超过要求,那么这一步就完成了。如果你需要更好的吞吐量的话,有一些JVM优化可以去做。

   这一步的另外一个输入就是,有多少内存可以供应用使用,就想前面说的GC最大化内存原则,越多可用的内存,性能就更好。这条原则不仅仅适用于吞吐量优化,同样适用于延迟优化。

   应用的吞吐量需求可能是无法满足的。如果是这种情况,那么就需要重新审视应用吞吐量的需求,应用就需要修改或者改变部署模型。如果上面的一种或者多种情况发生了,那么你需要重新进行前面的优化步骤。

   在前面的步骤里面,你可能使用吞吐量垃圾回收器解决了问题(通过-XX:+UseParallelOldGC或者-XX:+UsePrallelGC),或者你调整到并发垃圾回收器(CMS)来解决的问题。如果使用的CMS来解决的问题,下面有一些选项来提升应用的吞吐量,下面详细介绍。如果是使用的吞吐量垃圾回收器,我们将在CMS之后介绍。
   
   CMS吞吐量优化

   能够用来提升CMS吞吐量的选项数量有限,下面列出一些可以单独使用或者联合使用的选项:

   1、使用一些额外的命令选项,在后面的“额外的性能命令行选项”中详细介绍。
   2、增加young代的空间大小,增加young代的空间大小,可以减少MinorGC的频率,就能够减少在一段时间里面MinorGC占用的时间。
   3、增加old代的空间大小,增加old代的空间,可以减少CMS垃圾回收的频率,减少潜在的碎片,可以减少
stop-the-world垃圾回收。
   4、进一步优化young代堆大小,已经在前面的“优化延迟和响应时间”里面说过了,以及如何优化eden空间任务后和survivor空间大小以减少对象从young代移动到old也在前面已经说过了。需要注意的是,当优化eden和survivor空间大小的时候考虑到一些权衡。
   5、优化CMS周期的启动,也在前面说过了。
   
   任何上面提到的优化,或者组合使用上面的选择,都是减少垃圾回收器占用CPU时间,把CPU留给应用计算。前面两种选择,提供一种可能性来提升吞吐量,但是会有stop-the-world垃圾回收的风险,会增加延迟。

   作为指导,不考虑CMS,MinorGC的次数应该减少10%,你可能只能降低1%-3%。通常来讲,如果只能减少3%甚至更少,那么能够提升的吞吐量空间恐怕就有限了。

   吞吐量垃圾回收器优化

   优化吞吐量垃圾回收器的目标是避免FullGC或者理想情况下,避免在稳定状态下FullGC。这个需要优化对象的岁数,这个可以通过制定survivor空间优化完成。你可以让eden空间更大,可以减少MinorGC的次数。我知道当对象的任期或者岁数达到一定值的时候就会移动到old代,而这个任期就是对象经历MinorGC的次数,MinorGC的次数越少,对象任期增长越慢,就有可能被MinorGC回收掉,而不是进入old代。

   使用HotSpot VM的吞吐量垃圾回收器,可以通过-XX:+UseParallelOldGC和-XX:+UsePrallelGC,这样可以提供最好的吞吐量。吞吐量垃圾回收器利用了一种叫做自适应大小的特性,自适应大小是基于对象的分配和存活率来自动改变eden空间和survivor空间大小,目的是优化对象的岁数分布。自适应大小的企图是提供易用性,容易优化JVM,以致于提供可靠的吞吐量。自适应大小在大多数应用下,能够很好的工作,但是关闭自适应大小以及优化eden空间和survivor空间以及old代空间是一个探索提升应用吞吐量的一种办法。关闭自适应大小会改变应用的程序的灵活性,尤其是在修改应用程序,以及随着时间的推移应用的数据发生了变化。

   关闭自适应大小可以使用选项:

   -XX:-UseAdaptiveSizePolicy

   注意在“-XX”后面的“-”表明关闭UseAdapivieSizePolicy提供的特性。只有吞吐量垃圾回收器支持这个选项。在其他的垃圾回收器上使用这个选项是无用的。

   另外一个可选的命令行选项,可以产生关于survivor空间占用更详细的信息,关于survivor空间是否溢出,对象是否从young代移动到old代,选项是-XX:+PrintAdaptiveSizePolicy。这个选项最好和-XX:+PrintGCDetails以及-XX:+PrintGCDateStamps或者-XX:+PrintGCTimeStamps一起使用。下面是一个垃圾回收的例子-XX:+PrintGCDateStamps, -XX:PrintGCDetails, -XX:-UseAdaptiveSizePolicy (关闭自适应大小), 以及-XX:+PrintAdaptiveSizePolicy:

   2010-12-16T21:44:11.444-0600:
[GCAdaptiveSizePolicy::compute_survivor_space_size_and_thresh:
survived: 224408984
promoted: 10904856
overflow: false
[PSYoungGen: 6515579K->219149K(9437184K)]
8946490K->2660709K(13631488K), 0.0725945 secs]
[Times: user=0.56 sys=0.00, real=0.07 secs]

   和以前不同的是,以GCAdaptiveSizePolicy开头的一些额外信息输出来了,survived标签表明“to” survivor空间的对象字节数。在这个例子中,survivor空间占用量是224408984字节,但是移动到old代的字节数却有10904856字节。overflow表明young代是否有对象溢出到old代,换句话说,就是表明了“to” survivor是否有足够的空间来容纳从eden空间和“from”survivor空间移动而来的对象。为了更好的吞吐量,期望在应用处于稳定运行状态下,survivor空间不要溢出。

   为了开始优化,你应该关闭自适应大小以及获取在垃圾回收器日志里面额外的survivor空间统计信息,使用这两个选项-XX:-UseAdaptiveSizePolicy以及-XX:+PrintAdaptiveSizePolicy。这样提供了一些初始化的信息,以帮助做出优化决定。假如之前使用下面的命令行选项:

 
 -Xmx13g -Xms13g -Xmn4g -XX:SurvivorRatio=6 -XX:+UseParallelOldGC -XX:PrintGCDateStamps 
-XX:+PrintGCDetails


  那么,就应该如下一组命令行选项,来关闭自适应大小和捕获survivor空间统计信息:

 
 -Xmx13g -Xms13g -Xmn4g -XX:SurvivorRatio=6
-XX:+UseParallelOldGC -XX:PrintGCDateStamps -XX:+PrintGCDetails
-XX:-UseAdaptiveSizePolicy -XX:+PrintAdaptiveSizePolicy


   首先在应用稳定运行状态下寻找FullGC信息,包括日期和时间戳可以用来识别出应用是否从启动状态进入了稳定状态。举例,如果你知道应用启动需要30秒时间,那么在应用启动30秒之后才观察垃圾回收。

   观察FullGC信息,你可能会发现有一些短存活时间的对象移动到了old代空间,如果FullGC发生了,首先要确定是old代的空间是FullGC之后存活对象的1.5倍。如果有需要,增加old代的空间来保持1.5倍的指标,这样,可以保证old代有足够的空间来处理不在预期内的转移率(导致短的存活时间的对象移动到old代)或者一些未知的情况——导致了对象的转移过快,拥有这样的额外空间,可以延迟甚至可能能够阻止FullGC的发生。

   在确定了old代有足够的空间之后,就需要观察MinorGC的状况。首先需要观察survivor空间是否溢出,如果survivor空间溢出了,那么overflow标签会是true,否则,overload字段会是false。下面是一个survivor空间溢出的例子:

   2010-12-18T10:12:33.322-0600:
[GCAdaptiveSizePolicy::compute_survivor_space_size_and_thresh:
survived: 446113911
promoted: 10904856
overflow: true
[PSYoungGen: 6493788K->233888K(9437184K)]
7959281K->2662511K(13631488K), 0.0797732 secs]
[Times: user=0.59 sys=0.00, real=0.08 secs]

   如果survivor空间溢出,对象会再达到任期阀值或者消亡之前被移动到old代。换句话说,对象过快的移动到old代。频繁的survivor空间溢出会导致FullGC,下面说如何优化survivor。
   
优化survivor空间

   优化survivor空间的目标是保持或者老化短时间存活动的对象在young代中,一直到不得不移动到old代中。开始查看每一个MinorGC,尤其是存活的对象字节数。需要注意一点的是,为了避免应用启动的时候对象对后面分析的干扰,可以考虑放弃应用刚进入稳定状态的前面5到10个MinorGC信息。

   每次MinorGC之后的存活对象数量可以通过-XX:+PrintAdaptiveSizePolicy来查看。在下面的例子中,survivor对象的字节数是224408984。

2010-12-16T21:44:11.444-0600:   
[GCAdaptiveSizePolicy::compute_survivor_space_size_and_thresh:
survived: 224408984
promoted: 10904856
overflow: false
[PSYoungGen: 6515579K->219149K(9437184K)]
8946490K->2660709K(13631488K), 0.0725945 secs]
[Times: user=0.56 sys=0.00, real=0.07 secs]
[GCAdaptiveSizePolicy::compute_survivor_space_size_and_thresh:
survived: 224408984
promoted: 10904856
overflow: false
[PSYoungGen: 6515579K->219149K(9437184K)]
8946490K->2660709K(13631488K), 0.0725945 secs]
[Times: user=0.56 sys=0.00, real=0.07 secs]

   使用最大存活对象数量以及知道目标survivor空间的占用量,你可以决定出最差survivor空间大小,以使得让对象老化得更加高效。如果目标survivor空间的占用率没有通过-XX:TargetSurvivorRatio=<percent>指定,那么目标survivor空间的占用率是50%。

   首先为最差的场景优化survivor空间,这个需要找出在MinorGC之后最大的存活对象数量,注意可以忽略应用进入稳定状态前面的5到10个MinorGC。可以通过awk或者perl脚本来完成这项工作。

   调整survivor空间的大小,不是仅仅修改survivor空间的大小以使得比存活的对象字节数更大那么简单。需要记住的是,如果不增加young代的空间大小,而增加survivor空间的大小,会减少eden空间的大小,这样会导致频繁的MinorGC,从而是的对象的老化速度加快,更快的进入old代,又会导致FullGC。所以,需要同步增加young代的空间大小。如果不增加old的空间,那么就有可能造成频繁的FullGC甚至内存溢出错误。因此,如果有可以获取的空间,需要同步增加Java堆的空间。

   同样建议,HotSpot Vm使用默认的目标survivor空间占用率(50%),如果使用了-XX:TargetSurvivorRatio=<percent>,会使用<percent>作为MinorGC之后目标survivor空间占用率。如果survivor空间的占用率可能超过这个目标值,会在对象达到最大岁数之前把对象移动到old代去。

   通过一个例子详细说明,考虑用下面的命令选项:
-Xmx13g -Xms13g -Xmn4g -XX:SurvivorRatio=6
-XX:+UseParallelOldGC -XX:-UseAdaptiveSizePolicy
-XX:PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintAdaptiveSizePolicy


   总共的Java堆空间是13g,young代是4g,old代是9g,survivor空间的大小是4g/(6+2)=512M。假如一个应用的存活对象是470M,由于没有明确指定-XX:TargetSurvivorRatio=<percent>,那么默认的目标survivor空间占用率是50%,那么最小的survivor空间应该是940M,也就是最坏的情况,需要设置940M的survivor空间。

   从上面的例子来看,4g的young代空间被分隔成两个512M的survivor空间和一个3g的eden空间。刚才分析的最坏情况分配给survivor的空间是940M,差不多和1g相当。为了保持对象老化速率,即保持MinorGC的频率,eden空间需要保持在3g。因此,young代需要给每一个survivor空间1g内存以及3g的eden空间,那么young代需要增加到5g,也就是说young代需要增加1g空间,需要把-Xmn4g选项改成-Xmn5g选项。比较的理想的情况是,同步把Java堆的空间也增加1g。但是如果内存不够用,需要保证old代空间大小至少是存活对象的1.5倍。

   假设应用的内存需求满足,增加survivor空间占用后的命令选项是:

    
 -Xmx14g -Xms14g -Xmn5g -XX:SurvivorRatio=3
-XX:+UseParallelOldGC -XX:-UseAdaptiveSizePolicy
-XX:PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintAdaptiveSizePolicy


   old空间还是9g,young代的空间是5g,比之前大了1g,eden还是3g,每一个survivor空间是1g。

   你可能需要重复多次设定大小,直到满足内存占用的条件下到达吞吐量的峰值。吞吐的峰值一般都是在对象最有效的老化的时候的达到的。

   通常的建议是,吞吐量垃圾回收器的垃圾回收的开销应该小于5%。如果你只能把这个开销降低1%甚至更少,你可能需要使用除本章描述之外的特别努力和很大的开销来优化JVM。
  
优化Parallel GC线程

   吞吐量垃圾回收器的线程数的优化同样基于有多少应用运行在同一个系统里面以及硬件平台。就像前面的“优化CMS”里面提到的,如果是多个应用运行在同一个系统上面,建议使用比垃圾回收器默认使用的线程数更少的线程数,使用选项是-XX:ParallelGCThreads=<n>.

  另外,由于大量的垃圾回收线程同时执行,垃圾回收可能会严重影响其他应用的性能。由于Java 6 Update 23之后,默认的垃圾回收线程是执行Runtime.availableProcessors()获得的,如果这个方法的返回值小于等于8,那么就用这个返回值,如果比8更大,那么就取这个值的5/8。如果运行多个应用,可以根据应用的情况来分配线程数,如果应用的消耗是相当的,那么就用CPU的内核数除以应用数得到每一个应用可以分配的线程。如果应用的load不相当,那么就可以根据应用的实际情况来衡量和分配。
   
下一步
   如果你到这一步都还没有能够达到吞吐量的要求,那么可以尝试后面的“额外的性能选项”,如果还是无法达到,就只能修改应用或者JVM部署结构了。如果进行了修改应用或者修改了部署结构,你需要重新做前面的各个步骤。
   可能会用到的一些边缘场景,下面一节介绍。
   
分享到:
评论

相关推荐

    深入拆解一线大厂JVM 讲师:宋红康v1.1.mmap

    深入拆解一线大厂JVM 讲师:宋红康v1.1.mmap

    一步步优化JVM.docx

    【JVM 优化】是一个复杂而关键的过程,它涉及到对Java HotSpot虚拟机的深入理解和配置调整,以适应不同应用的性能需求。JVM优化不仅仅是调整参数,而是要结合具体应用的特点,找到最佳的运行环境。 优化现代JVM需要...

    WebSphere性能优化之二:JVM的运行效率.doc

    1. 针对吞吐量优化(-Xgcpolicy:optthruput):默认策略,适用于对长时间GC停顿不敏感,更注重整体性能的应用。 2. 针对停顿时间优化(-Xgcpolicy:optavgpause):通过并发GC来缩短停顿时间,牺牲部分吞吐量。 3. 分...

    JVM面试资料:JVM结构、JVM调优、四大垃圾回收算法、七大垃圾回收器

    JVM面试资料。 JVM结构:类加载器,执行引擎,本地方法接口,本地内存结构; 四大垃圾回收算法:复制算法、标记-清除算法、标记-整理算法、分代收集算法 七大垃圾回收器:Serial、Serial Old、ParNew、CMS、Parallel...

    jvm初识及JIT优化

    jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识...

    淘宝前台系统优化实践“吞吐量优化”

    控制服务器增长数量• 主题– 提升淘宝前台系统单服务器的QPS主要内容• QPS(吞吐量)三要素• 优化模板– 至少提升50%• 优化大数据的处理– 至少提升5%• 优化jvm参数– 资源太大,传百度网盘了,链接在附件中,...

    深入理解JVM内幕:从基本结构到Java 7新特性

    Java虚拟机(JVM)是Java程序的核心组件,它负责解析和执行Java字节码,使得Java具有“一次编译,到处运行”的特性。本文将深入探讨JVM的基本结构、执行流程,以及Java 7的新特性。 首先,JVM由Java API和JRE组成,...

    JVM内存模型深度剖析与优化.pdf

    JVM内存模型深度剖析与优化 JVM内存模型是Java虚拟机的核心组件之一,它直接影响着Java应用程序的性能和可靠性。本文将深入剖析JVM内存模型的结构和工作机理,并讨论如何优化JVM参数以提高Java应用程序的性能。 一...

    JVM详解:带书签超清文字版.pdf

    #### 一、Java与JVM概览 **1.1 Java定义** Java是一种广泛使用的面向对象的编程语言,以其强大的跨平台兼容性和安全性著称。Java程序能够运行在任何安装了Java运行环境(JRE)的设备上,这得益于Java虚拟机(JVM)...

    淘宝JVM优化实践-长仁.pdf

    《淘宝JVM优化实践》是一份深入探讨阿里巴巴集团核心系统研发部在JVM优化方面的实践经验的文档。这份文档主要涵盖了以下几个关键知识点: 1. **淘宝JVM优化背景**:随着淘宝、天猫等业务的快速发展,其Java应用规模...

    JVM性能优化(PPT)

    了解不同类型的垃圾收集器(如Serial、Parallel、CMS、G1、ZGC等)以及它们的工作机制,可以帮助我们选择合适的GC策略,减少停顿时间并提高吞吐量。 3. **内存调优** 调整JVM堆大小(年轻代和老年代)、新生代与老...

    深入jvm 内核-原理,诊断于优化视频教程

    - **案例2:大数据处理系统调优**:针对大数据处理系统,通过对JVM参数的调整以及合理的内存分配策略,显著提升了系统的处理速度和吞吐量。 #### 六、总结 本教程旨在帮助开发者深入了解JVM的工作原理及其内部机制...

    深入JVM内核—原理、诊断与优化

    《深入JVM内核—原理、诊断与优化》是一份深度探索Java虚拟机(JVM)的视频教程,旨在帮助开发者全面理解JVM的工作机制,掌握性能诊断技巧,并能进行有效的优化。本教程覆盖了从基础到高级的JVM主题,不仅适用于Java...

    JVM系列之性能调优参考手册(实践篇).pdf

    因为JVM内部经过大量的实践和优化,一般情况下我们只需要通过工具排查问题,而不轻易调整JVM参数。除非必要,否则不应随意更改JVM的默认行为,以免引入新的性能问题。因此,手册的实践操作部分对于开发者来说是作为...

    JVM性能优化:线程锁优化.docx

    Java虚拟机(JVM)性能优化的一个重要领域是线程锁优化,这直接影响到多线程应用程序的效率和并发性能。线程锁是确保多线程环境下数据一致性和线程安全的关键机制。以下是对线程锁优化的一些核心知识点的详细说明: ...

    jvm优化中文版

    综上所述,JVM优化是一个系统性工程,涵盖了从基础知识点到监控分析再到性能调优的一系列知识。理解JVM内部机制和不同JVM实现之间的区别,对于高效地优化Java应用性能是必不可少的。通过合理地应用各种优化手段,...

    推荐一些JVM原理,JVM调优,JVM内存模型,JAVA并发 电子书1

    标题中提到了JVM原理、JVM调优、JVM内存模型和JAVA并发,这些都是Java虚拟机(JVM)相关的核心概念。JVM是运行Java字节码的虚拟计算机,为Java提供了一个跨平台的环境,确保Java程序可以在不同的操作系统上运行而...

    Java虚拟机JVM性能优化(一):JVM知识总结

    总之,Java虚拟机的性能优化是一个涉及多方面知识的复杂过程,需要深入理解JVM的内部机制,结合实际应用特点,合理调整JVM参数,才能实现高效、稳定的运行。通过对JVM的深入学习和实践,开发者能够提升Java应用程序...

Global site tag (gtag.js) - Google Analytics