`
cantellow
  • 浏览: 848300 次
  • 性别: Icon_minigender_1
  • 来自: 草帽海贼团
社区版块
存档分类
最新评论

一次jvm调优实战

    博客分类:
  • JVM
 
阅读更多

 

总结

内存多占1G左右,CPU利用率没有明显变化,但随着CMS收集抖动,最高达40%CPU load平均高出1.0左右。

几乎0停顿,相比于之前每隔5分钟应用停顿3-4s,调优后的应用几乎没有停顿时间,每次”stop the world”youngGC引起,最高也不过200+ms

GC总时间开销显著减小20%多,吞吐量显著提升。

应用超过500ms的请求响应时间减少3%(一小时的观察,可能带有偶然性)

参数对比

调优前

-Dfile.encoding=UTF-8 -J-server -Xms8000M -Xmx8000M -Xmn5000M -J-Xss256K -J-XX:ThreadStackSize=256 -J-XX:StackShadowPages=8 -J-verbosegc -J-XX:+PrintGCDetails -J-XX:+PrintGCTimeStamps -XX:PermSize=128m -XX:MaxPermSize=128m -XX:+UseParallelGC 

调优后

-Dfile.encoding=UTF-8 -J-server -Xms10000M -Xmx10000M -Xmn5000M -XX:MaxTenuringThreshold=1 -XX:SurvivorRatio=30 -XX:TargetSurvivorRatio=50 -Xnoclassgc -Xss256K -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:PermSize=256m -XX:MaxPermSize=256m  -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=80 -XX:ParallelGCThreads=24 -XX:ConcGCThreads=24 -XX:+CMSParallelRemarkEnabled -XX:+CMSScavengeBeforeRemark -XX:+ExplicitGCInvokesConcurrent -XX:+UseTLAB -XX:TLABSize=64K

经验分享

在开始前,我们需要一些数据,因为jvm调优没有一个标准的答案,根据实际应用不同而不同,但也不是完全没有章法可言,从一个实际的应用,我们也可以找出一些规律来,找出一些比较公用的,比如下面三条:

1、应用平均和最大暂停时间(stop the world

2、吞吐量,真正运行时间/GC时间+真正运行时间),而相对的GC开销为:GC时间/GC时间+真正运行时间);

3、URL的请求响应时间

查看可以设置的所有参数

使用-XX:+PrintFlagsFinal参数可以查看当前版本的虚拟机所能设置的所有参数,还可以看到其默认值。我使用6u26版本的java虚拟机,一共有663个参数,很多参数不必完全搞懂什么意思,而且很多优化项在JDK6版本中已经默认开启,所以我们只需要了解一些常用的即可。

最大堆的设置

在单机web server的情况下,最大堆的设置建议在物理内存的1/22/3之间,如果是16G的物理内存的话,最大堆的设置应该在8000M-10000M之间,Java进程消耗的总内存肯定大于最大堆设置的内存:堆内存(Xmx+ 方法区内存(MaxPermSize+ 栈内存(Xss,包括虚拟机栈和本地方法栈)*线程数 + NIO direct memory + socket缓存区(receive37KBsend25KB+ JNI代码 + 虚拟机和GC本身 = java的内存。

 

我们经常碰到内存巨高的线上问题,留更多的内存给“意外情况”是一件好事也是一件坏事,好事是更多的内存可以给“错误”提供扩展空间,提升“容错性”,不至于马上宕机,但另一方面来说技术人员不会第一时间收到“吃swap”这个告警信息。

GC策略的选择

GC调优是JVM调优很重要的一步,当前比较成熟的GC基本上有三种选择,serialParallelCMS,大型互联网应用基本上选择后两种,但Parallel的暂停时间实在太长,以-Xmx8000M -Xmn5000M为例,平均一次youngGC需要100ms-200ms,而FullGC最长需要6s,平均也要4s,虽然当前没有哪种GC策略能完全做到没有暂停时间,但太长的“stop the world”时间也让人无法忍受。

 

serial ParallelGC都是完全stop the worldGC,而CMS分为六步骤:


初始标记(stop the world

1093.220: [GC [1 CMS-initial-mark: 4113308K(5120000K)] 4180786K(10080000K), 0.0732930 secs] [Times: user=0.07 sys=0.00, real=0.07 secs]

运行时标记(并发)

1094.275: [CMS-concurrent-mark: 0.980/0.980 secs] [Times: user=19.95 sys=0.51, real=0.98 secs]

运行时清理(并发)

1094.305: [CMS-concurrent-preclean: 0.028/0.029 secs] [Times: user=0.10 sys=0.02, real=0.03 secs]

CMS: abort preclean due to time 1099.643: [CMS-concurrent-abortable-preclean: 5.288/5.337 secs] [Times: user=12.64 sys=1.19, real=5.34 secs]

第二次标记(stop the world,这个例子remark前执行了一次youngGC

1099.647: [GC[YG occupancy: 3308479 K (4960000 K)]1099.648: [GC 1099.649: [ParNew: 3308479K->42384K(4960000K), 0.1420310 secs] 7421787K->4180693K(10080000K), 0.1447160 secs] [Times: user=2.69 sys=0.03, real=0.15 secs]

1099.793: [Rescan (parallel) , 0.0121000 secs]1099.805: [weak refs processing, 0.0664790 secs] [1 CMS-remark: 4138308K(5120000K)] 4180693K(10080000K), 0.2254870 secs] [Times: user=3.00 sys=0.05, real=0.23 secs]

运行时清理(并发)

1104.895: [CMS-concurrent-sweep: 4.970/5.020 secs] [Times: user=12.43 sys=1.05, real=5.02 secs]

复原(并发)

1104.908: [CMS-concurrent-reset: 0.012/0.012 secs] [Times: user=0.03 sys=0.01, real=0.01 secs]

 

要想知道应用真正的停顿时间,可以使用PrintGCApplicationStoppedTime参数:

63043.344: [GC [PSYoungGen: 5009217K->34119K(5049600K)] 5985479K->1034614K(8121600K), 0.1721890 secs] [Times: user=2.62 sys=0.01, real=0.18 secs]

Total time for which application threads were stopped: 0.1806210 seconds

Total time for which application threads were stopped: 0.0074870 seconds

这样看来,真正应用暂停的时间要比stop the world时间还要稍长一点点。

 

本次调优我基本上放弃了ParallelGC而选择了CMSCMSold区很大的时候绝对是个利器,它不仅能大幅降低应用“stop the world”时间,而且还能增加应用的吞吐量。

CMS还有一种增量模式:iCMS,适用于单CPU模式,会将回收动作分作小块进行,但会增加回收时间,降低吞吐量,对于多CPU来说,可以不用考虑这种模式。

 

PrintFlagsFinal参数可以得知CMSUseCMSCompactAtFullCollectionCMSParallelRemarkEnabled参数在JDK6里一直都是默认为true的,所以我们不必显示设置它。从维护角度来看,在设置参数之前,我们应该首先看看这个参数是不是默认已经开启了,如果默认已经开启了我们就不必要再显示设置它。

年轻代(edenSurvivor)、年老代的设置

选择了GC策略之后,年轻代和年老代的设置就很重要了,如果一味的追求响应时间,可以尽量把年轻代调大一点,youngGC的回收频率减小了,但回收时间也增大了,5000M的年轻代,平均回收时间在150+ms3000M的年轻代平均回收时间在90+ms

 

如果一味的增大年轻代,CMS前提下的年老代的威力也发挥不出来,更容易出现promotion failed,导致一次FullGC。但如果一味的调小年轻代,虽然单次回收时间减小,但回收频率会陡增,应用世界暂停时间也会增加,总体年轻代回收的时间也可能会增大,所以调整年轻代和年老代的比例就是一个找平衡的过程。

 

我的经验是年轻代的比例在2/84/8之间,具体情况要看实际应用情况而定。

我们都知道年轻代采用的是“copy”算法,有两个survivor空间,每次回收总有一个是空的,另一个存放的是前几次youngGC存留下来而且还不够提升到old资格的对象,所以有三个参数很重要:

-XX:MaxTenuringThreshold=15:对象晋升到old的年龄,parallelGC默认是15CMS默认是4,设置的越大,对象就越难进入到old区,youngGC反复copy的时间就会增大。

-XX:SurvivorRatio=8edensurvivor的比例,默认是8,也就是说如果eden2400M,那么两个survivor都为300M,如果MaxTenuringThreshold设置的很小,那么survivor区的使用率就会降低,反之,survivor的使用率就会增大。

-XX:TargetSurvivorRatio=80survivor空间的利用率,默认是50

 

如果设置SurvivorRatio65536MaxTenuringThreshold0就表示禁止使用survivor空间,在这种模式下,对象直接进入old区,而且我发现在这种模式下,photoresin启动时间大大减少,以前170s在这种模式下只需要90+s,足足降低了一半,因为这个,我顿时对这种模式产生的兴趣,但CMS的压力就增大了,威力根本发挥不出来了,GC的时间没有减少反而增加,remark的时间也增大到3s,最后不得不忍痛割爱放弃了这种模式。

 

-XX:+CMSScavengeBeforeRemark这个参数还蛮重要的,它的意思是在执行CMS remark之前进行一次youngGC,这样能有效降低remark的时间,之前我没有加这个参数,remark时间最大能达到3s,加上这个参数之后remark时间减少到1s之内。

 

另外,在13机器上(参照机器),我发现survivor空间并没有像预期的那样大(eden1/8),通过跟踪JVM的启动过程中发现,JVM在一定的条件下(可能跟parallelGC和默认SurvivorRatio有关)会动态调整survivor的大小,避免内存浪费。

  • 大小: 10.2 KB
分享到:
评论

相关推荐

    JVM调优实战(转)

    JVM调优实战 本文档旨在介绍JVM调优实战的各个方面,包括JVM内存、垃圾回收、性能优化等。通过对JVM内存结构、垃圾回收机制和性能优化策略的详细讲解,帮助读者深入理解JVM的工作原理和优化方法。 1. JVM内存结构 ...

    jvm调优实战经验

    【JVM调优实战经验】 在Java开发中,JVM(Java Virtual Machine)的调优是提高应用程序性能的关键环节。JVM调优涉及到对内存管理、垃圾回收机制以及相关参数的调整,以优化应用程序的运行效率和稳定性。本文将深入...

    JVM调优实战.doc

    《JVM调优实战》是一份深入探讨Java虚拟机(JVM)性能优化的文档,主要分为理论篇和实战篇两大部分。本文将详细解析其中的关键知识点。 理论篇首先介绍了JVM内存模型,将其比喻为一个多功能的养鱼塘。在这个比喻中...

    JVM调优实战

    JVM调优实战 介绍JVM调优的技巧相关参数的设置等等

    马老师jvm调优实战笔记

    马老师 JVM 调优实战笔记 JVM 调优是 Java 开发者们不可或缺的技能,它直接影响着 Java 应用程序的性能和稳定性。本笔记是马老师的 JVM 调优实战笔记,涵盖了 JVM 的概述、内存结构、堆内存、垃圾回收算法、JVM ...

    马士兵jvm调优笔记.docx

    - **复制算法**(Copying):将内存分为大小相等的两块,每次只使用其中一块,在垃圾回收时,将还活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。这种算法适用于新生代,因为每次回收都有大量...

    JVM调优.pdf

    #### 一、JVM调优概述 在现代软件开发中,Java虚拟机(JVM)作为Java应用程序的运行环境,对于提高应用程序的性能至关重要。JVM调优是指通过调整JVM的各种参数来优化Java应用程序的运行效率,减少资源消耗,提升...

    JVM调优篇.pdf

    JVM类加载机制是Java虚拟机中的一种机制,它负责加载Java类文件到内存中,以便执行Java程序。类加载机制分为五个阶段:加载、验证、准备、解析和初始化。 加载 加载是类加载过程中的一个阶段,这个阶段会在内存中...

    JVM入门到JVM 调优实战

    JVM调优包括选择合适的垃圾收集器、调整堆大小、设置新生代与老年代的比例、控制并发比等。例如,PS(Parallel Scavenge)和PO(Parallel Old)是两种常见的垃圾收集器组合,适用于不同的应用场景。实际调优过程中,...

    JVM调优总结.pdf

    JVM调优是一个复杂的过程,它涉及到对Java虚拟机内部工作原理的深刻理解。本文档总结了JVM调优的基础知识和一些核心概念,旨在帮助开发者更好地掌握Java程序的性能优化。 首先,文档提到了Java中的数据类型分为基本...

    JVM调优攻略.pdf

    5. 经过第一次Minor GC后仍然存活的对象会被移动到Survivor区,并且每次GC后年龄+1。 6. 当对象年龄达到一定阈值时,会被移至老年代。 #### 五、年轻代中的GC 在年轻代中,JVM需要确保To Survivor空间为空。在每次...

    java虚拟机(JVM)调优案例分析与Eclipse性能调优实战

    java虚拟机(JVM)调优案例分析与Eclipse性能调优实战

    jvm调优的实际应用

    《JVM调优实战解析》 在Java开发领域,JVM(Java Virtual Machine)是运行所有Java应用程序的基础,它的性能直接影响着程序的运行效率。因此,掌握JVM调优技术对于提升系统的稳定性和性能至关重要。本文将围绕"JVM...

    img JVM调优实战.pdf

    在对Java虚拟机(JVM)进行调优的过程中,我们首先...总体而言,JVM调优是一个系统化的过程,它需要对JVM的工作机制有深入的理解,并且结合实际应用进行测试和调优。有效的JVM调优能够显著提升Java应用的性能和稳定性。

    java高级篇:JVM调优.mp4

    分析常见的JVM调优技术及其相关条件,以及在实战中怎么使用到相关的技术进行调优,比较适合中高级开发进行学习,适合对JVM理解较深的开发学习

    【程序员必看】京东618亿级流量电商系统JVM调优实战

    2021最新版JVM调优实战全套教程!---吊打所有面试官!!offer轻松拿!

    个人总结之—JVM性能调优实战

    本总结旨在分享作者在实践中不断探索与总结的经验,为读者提供一份全面而实用的JVM调优指南。 #### 关键知识点 ##### 1. JVM基础知识 - **JVM结构与工作原理**:了解JVM的基本组成及其工作流程对于进行有效的性能...

    JVM实战-JVM调优案例分析与MyEclipse性能调优实战

    ### JVM实战-JVM调优案例分析与MyEclipse性能调优实战 #### 实验背景与目标 在现代软件开发过程中,提升开发效率是至关重要的环节之一。MyEclipse和Eclipse作为主流的集成开发环境(IDE),被广泛应用于Java项目的...

    java虚拟机(JVM)调优案例分析与MyEclipse性能调优实战

    java虚拟机(JVM)调优案例分析与MyEclipse性能调优实战

Global site tag (gtag.js) - Google Analytics