JVM调优

本文详细介绍了JVM调优的重要性、基本原则、目标、步骤及参数配置,强调了架构和代码优化的重要性。文章涵盖内存、延迟、吞吐量的调优策略,提供内存优化和延迟优化的实例,并给出了常用的JVM参数及其解释,旨在帮助开发者提升应用性能和系统稳定性。
摘要由CSDN通过智能技术生成

目录

1、性能调优

2、JVM调优的基本原则

3、JVM调优目标

4、JVM调优的步骤

5、JVM参数

6、内存优化示例

7、延迟优化示例

8、吞吐量调优

9、调优工具


1、性能调优

性能调优包含多个层次,比如:架构调优、代码调优、JVM调优、数据库调优、操作系统调优等。

架构调优和代码调优是JVM调优的基础,其中架构调优是对系统影响最大的。

性能调优基本上按照以下步骤进行:明确优化目标、发现性能瓶颈、性能调优、通过监控及数据统计工具获得数据、确认是否达到目标。
何时进行JVM调优

遇到以下情况,就需要考虑进行JVM调优了:

    Heap内存(老年代)持续上涨达到设置的最大内存值;
    Full GC 次数频繁;
    GC 停顿时间过长(超过1秒);
    应用出现OutOfMemory 等内存异常;
    应用中有使用本地缓存且占用大量内存空间;
    系统吞吐量与响应性能不高或下降。

2、JVM调优的基本原则

JVM调优是一个手段,但并不一定所有问题都可以通过JVM进行调优解决,因此,在进行JVM调优时,我们要遵循一些原则:

    大多数的Java应用不需要进行JVM优化;
    大多数导致GC问题的原因是代码层面的问题导致的(代码层面);
    上线之前,应先考虑将机器的JVM参数设置到最优;
    减少创建对象的数量(代码层面);
    减少使用全局变量和大对象(代码层面);
    优先架构调优和代码调优,JVM优化是不得已的手段(代码、架构层面);
    分析GC情况优化代码比优化JVM参数更好(代码层面);

通过以上原则,我们发现,其实最有效的优化手段是架构和代码层面的优化,而JVM优化则是最后不得已的手段,也可以说是对服务器配置的最后一次“压榨”。

3、JVM调优目标

调优的最终目的都是为了令应用程序使用最小的硬件消耗来承载更大的吞吐。jvm调优主要是针对垃圾收集器的收集性能优化,令运行在虚拟机上的应用能够使用更少的内存以及延迟获取更大的吞吐量。

    延迟:GC低停顿和GC低频率;
    低内存占用;
    高吞吐量;

其中,任何一个属性性能的提高,几乎都是以牺牲其他属性性能的损为代价的,不可兼得。具体根据在业务中的重要性确定。

4、JVM调优的步骤

一般情况下,JVM调优可通过以下步骤进行:

    分析GC日志及dump文件,判断是否需要优化,确定瓶颈问题点;
    确定JVM调优量化目标;
    确定JVM调优参数(根据历史JVM参数来调整);
    依次调优内存、延迟、吞吐量等指标;
    对比观察调优前后的差异;
    不断的分析和调整,直到找到合适的JVM参数配置;
    找到最合适的参数,将这些参数应用到所有服务器,并进行后续跟踪。

以上操作步骤中,某些步骤是需要多次不断迭代完成的。一般是从满足程序的内存使用需求开始的,之后是时间延迟的要求,最后才是吞吐量的要求,要基于这个步骤来不断优化,每一个步骤都是进行下一步的基础,不可逆行之。


5、JVM参数

   5.1 通用 JVM 参数

       5.1.1 -server

            如果不配置该参数,JVM会根据应用服务器硬件配置自动选择不同模式,server模式启动比较慢,但是运行期速度得到了优化,适合于服务器端运行的JVM

        5.1.2 -client

            启动比较快,但是运行期相应没有server模式的优化,适合于个人PC的服务开发和测试

        5.1.3 -Xmx

            设置java heap的最大值,默认是机器物理内存的1/4.这个值决定了最多可用的Java堆内存:分配过少就会在应用中需要大量内存作为缓存或者临时对象时出现OOM(Out Of Memory)的问题:如果分配过大,name就会因PermSize过小而引起的另一种OutOfMemory。所以如何配置还是根据运行过程中的分析和计算来确定,如果不能确定还是采用默认的配置

        5.1.4 -Xms

            设置Java堆初始化时的大小,默认情况是机器物理内存的1/64.这个主要是根据应该用启动时消耗的资源决定,分配少了申请起来会降低运行速度,分配多了也浪费。

        5.1.5 -XX:PermSize

            初始化永久内存区域大小。永久内存区域全称是 Permanent Generation space,是指内存的永久保存区域,程序运行期不对 PermGen space 进行清理,所以如果你的 APP 会 LOAD 很多 CLASS 的话,就很可能出现 PermGen space错误。这种错误常见在 web 服务器对 JSP 进行 pre compile 的时候。 如果你的 WEB APP 下用了大量的第三方 jar, 其大小超过了 jvm 默认的 PermSize 大小(4M)那么就会产生此错误信息了。

        5.1.6 -XX:MaxPermSize

            设置永久内存区域最大大小

        5.1.7 -Xmn

            直接设置青年代大小。整个 JVM 可用内存大小=青年代大小 + 老年代大小 + 持久代大小 。持久代一般固定大小为 64m,所以增大年轻代后,将会减小老年代大小。此值对系统性能影响较大,Sun 官方推荐配置为整个堆的3/8。按照 Sun 的官方设置比例,则上面的例子中年轻代的大小应该为 2048*3/8=768M

        5.1.8 -XX:NewRatio

            控制默认的 Young 代的大小,例如,设置-XX:NewRatio=3 意味着 Young 代和老年代的比率是 1:3。换句话说,Eden 和 Survivor 空间总和是整个堆大小的 1/4。

                如图中的实际设置,-XX:NewRatio=2,-Xmx=2048,则年轻代和老年代的分配比例为 1:2,即年轻代的大小为 682M, 而老年代的大小为 1365M。查看实际系统的 jvm 监控结果为:
                内存池名称: Tenured Gen
                Java 虚拟机最初向操作系统请求的内存量: 3,538,944 字节
                Java 虚拟机实际能从操作系统获得的内存量: 1,431,699,456 字节
                Java 虚拟机可从操作系统获得的最大内存量: 1,431,699,456 字节。请注意,并不一定能获得该内存量。
                Java 虚拟机此时使用的内存量: 1,408,650,472 字节
                即:1,408,650,472 字节=1365M,证明了上面的计算是正确的

        5.1.9 -XX:SurvivorRatio

            设置年轻代中 Eden 区与 Survivor 区的大小比值。设置为 4,则两个 Survivor 区与一个 Eden 区的比值为 2:4,一个 Survivor 区占整个年轻代的 1/6。越大的 survivor 空间可以允许短期对象尽量在年青代消亡;如果 Survivor 空间太小,Copying 收集将直接将其转移到老年代中,这将加快老年代的空间使用速度,引发频繁的完全垃圾回收【SurvivorRatio 的值设为 3,Xmn 为 768M,则每个 Survivor 空间的大小为 768M/5=153.6M。】

        5.1.10 -XX:NewSize

            为了实现更好的性能,您应该对包含短期存活对象的池的大小进行设置,以使该池中的对象的存活时间不会超过一个垃圾回收循环。新生成的池的大小由 NewSize 和 MaxNewSize 参数确定。通过这个选项可以设置 Java 新对象生产堆内存。在通常情况下这个选项的数值为 1024 的整数倍并且大于 1MB。这个值的取值规则为,一般情况下这个值-XX:NewSize 是最大堆内存(maximum heap size)的四分之一。增加这个选项值的大小是为了增大较大数量的短生命周期对象。增加 Java 新对象生产堆内存相当于增加了处理器的数目。并且可以并行地分配内存,但是请注意内存的垃圾回收却是不可以并行处理的。作用跟-XX:NewRatio 相似, -XX:NewRatio 是设置比例而-XX:NewSize是设置精确的数值。

        5.1.11 -XX:MaxNewSize

            通过这个选项可以设置最大 Java 新对象生产堆内存。通常情况下这个选项的数值为 1 024 的整数倍并且大于1MB,其功用与上面的设置新对象生产堆内存-XX:NewSize 相同。一般要将 NewSize 和 MaxNewSize 设成一致。

        5.1.12 -XX:MaxTenuringThreshold

            设置垃圾最大年龄。如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入老年代。对于老年代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在 Survivor 区进行多次复制,这样可以增加对象在年轻代的存活时间,增加在年轻代即被回收的概率【-XX:MaxTenuringThreshold 参数被设置成 5,表示对象会在 Survivor 区进行 5 次复制后如果还没有被回收才会被复制到老年代】

        5.1.13 -XX:GCTimeRatio

            设置垃圾回收时间占程序运行时间的百分比。该参数设置为 n 的话,则垃圾回收时间占程序运行时间百分比的公式为 1/(1+n) ,如果 n=19 表示 java 可以用 5%的时间来做垃圾回收,1/(1+19)=1/20=5%。

        5.1.14 -XX:TargetsurvivorRatio

            该值是一个百分比,控制允许使用的救助空间的比例,默认值是 50。该参数设置较大的话可提高对 survivor 空间的使用率。当较大的堆栈使用较低的 SurvivorRatio 时,应增加该值到 80 至 90,以更好利用救助空间。

        5.1.15 -Xss

            设置每个线程的堆栈大小,根据应用的线程所需内存大小进行调整,在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000 左右。当这个选项被设置的较大(>2MB)时将会在很大程度上降低系统的性能。因此在设置这个值时应该格外小心,调整后要注意观察系统的性能,不断调整以期达到最优。JDK5.0 以后每个线程堆栈大小为 1M,以前每个线程堆栈大小为 256K。

        5.1.16 -Xnoclassgc

            这个选项用来取消系统对特定类的垃圾回收。它可以防止当这个类的所有引用丢失之后,这个类仍被引用时不会再一次被重新装载,因此这个选项将增大系统堆内存的空间。禁用类垃圾回收,性能会高一点;

    5.2 串行收集器参数

        5.2.1 -XX:+UseSerialGC:

            设置串行收集器

    5.3 并行收集器参数

        5.3.1 -XX:+UseParallelGC:

            选择垃圾收集器为并行收集器,此配置仅对年轻代有效,即上述配置下,年轻代使用并行收集,而老年代仍旧使用串行收集。采用了多线程并行管理和回收垃圾对象,提高了回收效率,提高了服务器的吞吐量,适合于多处理器的服务器。

        5.3.2 -XX:ParallelGCThreads

            配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。

        5.3.3 -XX:+UseParallelOldGC:

            采用对于老年代并发收集的策略,可以提高收集效率。JDK6.0 支持对老年代并行收集。

        5.3.4 -XX:MaxGCPauseMillis

            设置每次年轻代并行收集最大暂停时间,如果无法满足此时间,JVM 会自动调整年轻代大小以满足此值。

        5.3.5 -XX:+UseAdaptiveSizePolicy

            设置此选项后,并行收集器会自动选择年轻代区大小和相应的 Survivor 区比例,以达到目标系统规定的最低响应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

    5.4 并发收集器参数

        5.4.1 -XX:+UseConcMarkSweepGC

            指定在 老年代 使用 concurrent cmark sweep gc。gc thread 和 app thread 并行 ( 在 init-mark 和 remark 时pause app thread)。app pause 时间较短 , 适合交互性强的系统 , 如 web server。它可以并发执行收集操作,降低应用停止时间,同时它也是并行处理模式,可以有效地利用多处理器的系统的多进程处理。

        5.4.2 -XX:+UseParNewGC

            指定在 New Generation 使用 parallel collector, 是 UseParallelGC 的 gc 的升级版本 , 有更好的性能或者优点 , 可以和 CMS gc 一起使用

        5.4.3 -XX:+UseCMSCompactAtFullCollection:

            打开对老年代的压缩。可能会影响性能,但是可以消除碎片,在 FULL GC 的时候, 压缩内存, CMS 是不会移动内存的, 因此, 这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯

        5.4.4 -XX:+CMSIncrementalMode

            设置为增量模式。适用于单 CPU 情况

        5.4.5 -XX:CMSFullGCsBeforeCompaction

            由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次 GC 以后对内存空间进行压缩、整理。

        5.4.6 -XX:+CMSClassUnloadingEnabled

            使 CMS 收集持久代的类,而不是 fullgc

        5.4.7 -XX:+CMSPermGenSweepingEnabled

            使 CMS 收集持久代的类,而不是 fullgc。

        5.4.8 -XX:-CMSParallelRemarkEnabled

            在使用 UseParNewGC 的情况下 , 尽量减少 mark 的时间。

        5.4.9 -XX:CMSInitiatingOccupancyFraction

            说明老年代到百分之多少满的时候开始执行对老年代的并发垃圾回收(CMS),这个参数设置有很大技巧,基本上满足公式:

            (Xmx-Xmn)(100-CMSInitiatingOccupancyFraction)/100>=Xmn

            时就不会出现 promotion failed。在我的应用中 Xmx 是 6000,Xmn 是 500,那么 Xmx-Xmn 是 5500 兆,也就是老年代有 5500 兆,CMSInitiatingOccupancyFraction=90 说明老年代到 90%满的时候开始执行对老年代的并发垃圾回 收(CMS),这时还剩 10%的空间是 550010%=550 兆,所以即使 Xmn(也就是年轻代共 500 兆)里所有对象都搬到老年代里,550 兆的空间也足够了,所以只要满足上面的公式,就不会出现垃圾回收时的 promotion failed;如果按照 Xmx=2048,Xmn=768 的比例计算,则 CMSInitiatingOccupancyFraction 的值不能超过 40,否则就容易出现垃圾回收时的 promotion failed。

                子主题 1

        5.4.10 -XX:+UseCMSInitiatingOccupancyOnly

            指示只有在老年代在使用了初始化的比例后 concurrent collector 启动收集

        5.4.11 -XX:SoftRefLRUPolicyMSPerMB

            相对于客户端模式的虚拟机(-client 选项),当使用服务器模式的虚拟机时(-server 选项),对于软引用(softreference)的清理力度要稍微差一些。可以通过增大-XX:SoftRefLRUPolicyMSPerMB 来降低收集频率。默认值是1000,也就是说每秒一兆字节。Soft reference 在虚拟机中比在客户集中存活的更长一些。其清除频率可以用命令行参数 -XX:SoftRefLRUPolicyMSPerMB=<N> 来控制,这可以指定每兆堆空闲空间的 soft reference 保持存活(一旦它不强可达了)的毫秒数,这意味着每兆堆中的空闲空间中的 soft reference 会(在最后一个强引用被回收之后)存活 1 秒钟。注意,这是一个近似的值,因为 soft reference 只会在垃圾回收时才会被清除,而垃圾回收并不总在发生。

        5.4.12 -XX:LargePageSizeInBytes

            内存页的大小, 不可设置过大,会影响 Perm 的大小。

        5.4.13 -XX:+UseFastAccessorMethods

            原始类型的快速优化,get,set 方法转成本地代码。

        5.4.14 -XX:+DisableExplicitGC

            禁止 java 程序中的 full gc, 如 System.gc() 的调用。 最好加上防止程序在代码里误用了,对性能造成冲击。

        5.4.15 -XX:+AggressiveHeap

            特别说明下:(我感觉对于做 java cache 应用有帮助)试图是使用大量的物理内存长时间大内存使用的优化,能检查计算资源(内存, 处理器数量) 至少需要 256MB 内存大量的 CPU/内存, (在 1.4.1 在 4CPU 的机器上已经显示有提升)

        5.4.16 -XX:+AggressiveOpts

            加快编译

        5.4.17 -XX:+UseBiasedLocking

            锁机制的性能改善。


6、内存优化示例

当JVM运行稳定之后,触发了FullGC我们一般会拿到如下信息:

以上gc日志中,在发生fullGC之时,整个应用的堆占用以及GC时间。为了更加精确需多次收集,计算平均值。或者是采用耗时最长的一次FullGC来进行估算。上图中,老年代空间占用在93168kb(约93MB),以此定为老年代空间的活跃数据。则其他堆空间的分配,基于以下规则来进行。

    java heap:参数-Xms和-Xmx,建议扩大至3-4倍FullGC后的老年代空间占用。
    永久代:-XX:PermSize和-XX:MaxPermSize,建议扩大至1.2-1.5倍FullGc后的永久带空间占用。
    新生代:-Xmn,建议扩大至1-1.5倍FullGC之后的老年代空间占用。
    老年代:2-3倍FullGC后的老年代空间占用。

基于以上规则,则对参数定义如下:

java -Xms373m -Xmx373m -Xmn140m -XX:PermSize=5m -XX:MaxPermSize=5m

7、延迟优化示例

对延迟性优化,首先需要了解延迟性需求及可调优的指标有哪些。

    应用程序可接受的平均停滞时间: 此时间与测量的Minor
    GC持续时间进行比较。可接受的Minor GC频率:Minor
    GC的频率与可容忍的值进行比较。
    可接受的最大停顿时间:最大停顿时间与最差情况下FullGC的持续时间进行比较。
    可接受的最大停顿发生的频率:基本就是FullGC的频率。

其中,平均停滞时间和最大停顿时间,对用户体验最为重要。对于上面的指标,相关数据采集包括:MinorGC的持续时间、统计MinorGC的次数、FullGC的最差持续时间、最差情况下,FullGC的频率。

如上图,Minor GC的平均持续时间0.069秒,MinorGC的频率为0.389秒一次。

新生代空间越大,Minor GC的GC时间越长,频率越低。如果想减少其持续时长,就需要减少其空间大小。如果想减小其频率,就需要加大其空间大小。

这里以减少了新生代空间10%的大小,来减小延迟时间。在此过程中,应该保持老年代和持代的大小不变化。调优后的参数如下变化:

java -Xms359m -Xmx359m -Xmn126m -XX:PermSize=5m -XX:MaxPermSize=5m

8、吞吐量调优

吞吐量调优主要是基于应用程序的吞吐量要求而来的,应用程序应该有一个综合的吞吐指标,这个指标基于整个应用的需求和测试而衍生出来的。

评估当前吞吐量和目标差距是否巨大,如果在20%左右,可以修改参数,加大内存,再次从头调试,如果巨大就需要从整个应用层面来考虑,设计以及目标是否一致了,重新评估吞吐目标。

对于垃圾收集器来说,提升吞吐量的性能调优的目标就是尽可能避免或者很少发生FullGC或者Stop-The-World压缩式垃圾收集(CMS),因为这两种方式都会造成应用程序吞吐降低。尽量在MinorGC 阶段回收更多的对象,避免对象提升过快到老年代。

9、调优工具

借助GCViewer日志分析工具,可以非常直观地分析出待调优点。可从以下几方面来分析:

Memory,分析Totalheap、Tenuredheap、Youngheap内存占用率及其他指标,理论上内存占用率越小越好;

Pause,分析Gc pause、Fullgc pause、Total pause三个大项中各指标,理论上GC次数越少越好,GC时长越小越好;


 激动的心,颤抖的手

 借用客官你们发财的小手,给个鼓励吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LiarBoy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值