• 笔记:Java 性能优化权威指南 第7章 JVM调优入门


    一、调优工作流程

    (1)、划分系统需求优先级(内存消耗、响应时间、吞吐量、可用性、可管理性、启动时间等)

    (2)、选择JVM部署模式:单JVM还是多JVM,32位或64位,64位指针压缩

    (3)、选择JVM Runtime:-client 还是 server 模式,垃圾回收器Parallel 还是 CMS

    (4)、调整内存使用

    (5)、调整时间延迟

    (6)、调整吞吐量


    二、应用程序的系统需求

    1、可用性:例子:即使某个组件发生了不可预料的失效,也不会导致整个程序无法使用。

    2、可管理性:例子:由于人力资源有限,应该部署尽量少的JVM数量。

    3、吞吐量:例子:程序每秒应该完成2500次事务。

    4、响应延时:例子:程序应该在60毫秒内完成请求的处理工作。

    5、内存占用:例子:程序在8G内存的系统上单例方式运行,或者24G内存系统上3个实例运行。

    6、启动时间:例子:应用程序初始化在15秒以内。


    三、对系统需求进行分级

    比如互联网、分布式,对响应延时要求高。


    四、选择JVM部署模式

    单JVM:管理方便,但是存在单点故障。

    多JVM:高可用性、低延时,监控、管理、维护更难。


    五、选择JVM 运行模式

    Client模式:启动快、占用内存少、JIT编码器生成代码快。

    Server模式:提供了更复杂的生成代码优化功能。一般服务器使用Server模式。


    Linux中,如内存小于2G:使用32位模式

    Linux中,如内存在2G~32G:使用64位模式,并启用指针压缩,-d64 -XX:+UseCompressedOops

    Linux中,如内存大于32G:使用64位模式,不启用指针压缩,-d64  


    一般情况下,Parallel 收集器能达到应用程序停顿要求,若再需要低延时,则转向CMS收集器。


    六、垃圾收集调优基础

    主要调节三个属性:吞吐量、延时、内存占用。一般提高某一个属性都会消耗另外两个属性。

    调解的三个原则:

    (1)、Minor GC 应该尽可能多收集垃圾,尽量减少Full GC。

    (2)、Java Heap空间越大,垃圾收集效果越好,吞吐量和延时越好

    (3)、三个属性中选择两个进行调优 (GC 调优3选2原则)


    调优应该开启GC 日志:-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -XXloggc:/opt/jvm-gc.log

    -XX:PrintGCDateStamps   打印日历

    -XX:PrintGCApplicationConcurrentTime       打印两个安全点之间应用程序运行的时间,判定应用程序是否在执行   

    -XX:PrintGCApplicationStoppedTime            打印由于安全点操作而阻塞的时间,确认延迟是来自安全点还是其他问题

    -XX:PrintSatePointStastics                              打印安全点统计信息



    七、确定内存占用


    1、JVM布局


    (1)、关注吞吐量、低延时的应用应该把 -Xmx  和 -Xms 设为同一个值,避免扩展或缩减新生代或老生代空间而进行的Full GC。

    (2)、使用-Xmn 将新生代的初始值和最大值设为同一值。

    (3)、关注性能的应用应该将 -XX:PermSize 和 -XX:MaxPermSize 设为同一值,因为永久代大小调整需要Full GC。

    (4)、如果老生代或永久代内存不足,不管其他代内存是否充足,都会发生Full GC。


    2、Heap 大小调优步骤


    (1)、先使用JVM默认配置、或者先指定-Xmx和-Xms,将程序推进到稳定状态中,等程序出现了老年代、永久代的内存溢出,则增加老生代大小 (-Xms 和 -Xmx ),增加永久代大小(-XX:PermSize 和 -XX:MaxPermSize),如此迭代,直到不再出现内存溢出,程序稳定。


    (2)、计算活跃数据大小:即程序运行与稳定状态后,老年代、永久带分别占用Java Heap的大小。可使用 VisualVM 触发Full GC,看日志。


    (3)、配置堆空间初始堆大小:

    -Xmx 和 -Xms :                                 Heap总容量为 老生代活跃数据大小的3~4

    -Xmn:                                                    新生代容量为 老生代活跃数据大小的1~1.5倍,即老生代容量为老生代活跃数据的2~3倍

    -XX:PermSize -XX:MaxPermSize :   永久代活跃数据大小的1.5


    八、确定调优延迟/响应性


    1、从日志中统计以下数据

    Minor GC的持续时间、Minor GC的频率、Full GC的最长时间、Full GC的最大频率


    2、优化新生代大小

    若Minor GC 时间过长,减少新生代空间。

    若Minor GC 过于频繁,增大新生代空间。

    例子:

    假设应用程序的延迟性要求是40毫秒, Minor GC 频率为54毫秒,超过了要求,所以应该减少内存。

    假设应用程序的频率要求是5秒一次, Minor GC 频率为2.147秒一次,超过了要求,所以应该增大内存。


    注意:

    (1)、调节-Xmn 的时候需要同时调解 -Xms 和 -Xmx, 保证老年代大小不变。

    (2)、老生代容量至少为老生代活跃数据的1.5倍

    (3)、新生代大小至少为Heap大小的10%  (过小会导致Minor GC 频繁)

    (5)、增大Java Heap大小时候,不可以超过JVM 可用的物理内存数 (使用虚拟内存性能底下)


    3、优化老生代大小

    Full GC 的频率过高,则增大老年代大小,增加老年代大小的同时应该保持新生代大小不变。

    Full GC 的延时过长,增大老年代一般无效,需要切换使用CMS 调优器,-XX:UseConcMarkSweepGC,并继续调优


    可以查看Minor GC 的提升率,预测Full GC 的频率:

     Minor GC 的日志可以查看 Heap 的总容量: totalHeap 

    查看新生代的总容量: totalYoung 

    查看MInor GC'的时间间隔: minorTime

    计算老生代的容量:totalOld  = totalHeap - totalYoung 

    在Full GC的日志中可以查看老生代的活跃数据大小:activeOld 

    计算老生代的空闲空间:freeOld =totalOld  - activeOld 

    Minor GC 中观察每次新生代的剩余空间 freeYoung1、freeYoung2、freeYoung3

    Minor GC 中观察每次Heap的剩余空间 freeTotal1、freeTotal2、freeTotal3、freeTotal4

    于是可以求得每次MinorGC后提升到老年代占用空间:occupancyOld1=  freeTotal1- freeYoung1,  occupancyOld2=  freeTotal2- freeYoung2,......

    求得每次Minor GC提升到老生代的数据量:promoteOld1 = occupancyOld2-occupancyOld1;  promoteOld2 = occupancyOld3-occupancyOld2

    求得每次Minor GC提升到老生代数据量的平均值 promoteOld

    老生代空间占满需要的次数:promoteTotalCount = activeOld /promoteOld;

    老生代空间占满需要的时间:promoteTotalTime = promoteCount * minorTime

    promoteTotalTime 即可推测出Full GC 多久发生一次。


    4、为CMS调优延迟


    采用CMS的绝对最差延迟比Parallel的最差延迟更长,所以,从Parallel转换到CMS的时候,需要将老年代空间增大20%-30%.

    处理CMS的老年代碎片问题:减少对象从新生代提升到老年代的比率。


    5、调优Survivor空间


    Survivor过小会将很多对象提升到老生代,引发Full GC。

    (1)、通过命令 -XX:+PrintTenuringDistribution 可以观察Survivor空间对象的年龄组成。


    (2)、调优Survivor与Eden的比例:-XX:SurvivorRatio   

    每个Survivor容量 = 新生代容量 / (SurvivorRadio +2) ;

    通过 -XX:PrintTenuringDistribution 可以观察所有对象年龄的总大小,统计Survivor空间存活的对象总大小,将Survivor大小设为存活对象大小的2倍 (默认 -XX: TargetSurvivorRatio=50 )。

    增大Survivor的同时,应该保持Eden区不变,即需要同时增大-Xmn,同时还要老年代大小不变,故而还需要增大-Xmx 和 -Xms


    (3)、调优对象提升到老年代的年龄:-XX:MaxTenuringThreshold  

    一般使用默认值 -XX:MaxTenuringThreshold =15 。通常情况下,宁可对象在Survivor之间多次复制,也不要将其匆匆复制到老年代。 

    可以  -XX:PrintTenuringDistribution  监控


    6、调优CMS发生时刻  -XX:CMSInitiatingOccupancyFraction=<percent> -XX:CMSInitialingOccupancyOnly 

    CMSInitiatingOccupancyFraction 过大,则CMS 启动太晚,则执行时间长,甚至内存溢出失败。

    CMSInitiatingOccupancyFraction 过小,则CMS 启动太早,则执行频繁,几乎回收不到垃圾。


    CMSInitiatingOccupancyFraction 起码应该大于 老年代活跃对象大小/老年代总大小,否则将陷入死循环

    CMSInitiatingOccupancyFraction 一般大于 1.5倍的 老年代活跃对象大小/老年代总大小。作为初始值,参照日志继续调优。


    最好同时设置 -XX:CMSInitialingOccupancyOnly ,使得JVM 一直使用这个percent,否则JVM会在使用了第一遍之后又会转向自适应。


    7、禁止 显示垃圾收集

    -XX:+DisableExplicitGC 禁止System.gc().


    8、调优永久代垃圾收集

    CMS 默认不开启垃圾回收,开启需要设置:-XX:CMSClassUnloadingEnabled

    同时配置使用:-XX:CMSPermGenSweepingEnabled  -XX:CMSInitiatingPermOccupanyFraction=<percent>  -XX:UseCMSInitiatingOccupancyOnly


    9、调优CMS停顿时间

    初始标记是单线程的,极少占用时间。

    重新标记是多线程的,占用较多时间,可以调节线程数:+XX:ParallelGCThreads=<n>,默认值 = 8+ (处理器核数-8)*5/8  ,约为 3 + 处理器数*0.6 。可以调小这个值,减少停顿。

    强制重新标记之前进行Minor GC: -XX:CMSScavengeBeforeRemark

    减少大量引用对象、可终结对象带来的垃圾收集持续时间:-XX:+ParallelRefProcEnabled.


    九、调优吞吐量


    1、CMS的吞吐量调优

    增大新生代,减少Minor GC的次数

    增大老生代,减少CMS次数

    优化Survivor,减少提升到老生代对象

    优化CMS启动条件,晚点启动。

    一般CMS 包括Minor GC所带来的开销应该小于10%, 可以优化减少到1%-3%。


    2、Parallel的吞吐量调优

    禁用自适应:-XX:-UseAdaptiveSizePolicy

    调优Survivor空间:

    调优并行垃圾收集器:

    在NUMA系统部署:使用 -XX:+UseNUMA

    十、其他性能命令

    1、开启实验性的优化 (有风险,可能不稳定)

    -XX:+AggressiveOpts


    2、逃逸分析优化

    -XX:+DoEscapeAnalysis

    JVM会采用以下方式优化:对象展开、标量替换、栈上分配、消除同步、消除垃圾收集的读写屏障


    3、偏向锁

    -XX:+UseBiasedLocjing


    4、大页面支持

    -XX:+UseLargePages




     

  • 相关阅读:
    web 4.0规范畅想
    统计图(折线,饼图等)避免多个统计图显示冲突
    轮播每页显示4条数据(待写)
    2022年目标
    WSL安装mpich 3.4.2
    an error ocurred during local report processing
    一个winform中多线程的例子
    INSERT INTO 语句的语法错误
    MVC学习笔记
    去掉vsto生成的任务窗格
  • 原文地址:https://www.cnblogs.com/leeeee/p/7276241.html
Copyright © 2020-2023  润新知