Java教程:9种常见的CMS GC问题详解(十四)

开课吧开课吧锤锤2021-03-24 11:16

    Java编程语言是一种简单、面向对象、分布式、解释型、健壮安全、与系统无关、可移植、高性能、多线程和动态的语言。如今Java已经广泛应用于各个领域的编程开发。

Java

    5.总结

    在这里,我们把整个文章内容总结一下,方便大家整体地理解回顾。

    5.1处理流程(SOP)

    下图为整体GC问题普适的处理流程,重点的地方下面会单独标注,其他的基本都是标准处理流程,此处不再赘述,最后在整个问题都处理完之后有条件的话建议做一下复盘。

Java

    制定标准:这块内容其实非常重要,但大部分系统都是缺失的,笔者过往面试的同学中只有不到一成的同学能给出自己的系统GC标准到底什么样,其他的都是用的统一指标模板,缺少预见性,具体指标制定可以参考3.1中的内容,需要结合应用系统的TP9999时间和延迟、吞吐量等设定具体的指标,而不是被问题驱动。

    保留现场:目前线上服务基本都是分布式服务,某个节点发生问题后,如果条件允许一定不要直接操作重启、回滚等动作恢复,优先通过摘掉流量的方式来恢复,这样我们可以将堆、栈、GC日志等关键信息保留下来,不然错过了定位根因的时机,后续解决难度将大大增加。当然除了这些,应用日志、中间件日志、内核日志、各种Metrics指标等对问题分析也有很大帮助。

    因果分析:判断GC异常与其他系统指标异常的因果关系,可以参考笔者在3.2中介绍的时序分析、概率分析、实验分析、反证分析等4种因果分析法,避免在排查过程中走入误区。

    根因分析:确实是GC的问题后,可以借助上文提到的工具并通过5why根因分析法以及跟第三节中的九种常见的场景进行逐一匹配,或者直接参考下文的根因鱼骨图,找出问题发生根因,最后再选择优化手段。

    5.2根因鱼骨图

    送上一张问题根因鱼骨图,一般情况下我们在处理一个GC问题时,只要能定位到问题的“病灶”,有的放矢,其实就相当于解决了80%,如果在某些场景下不太好定位,大家可以借助这种根因分析图通过排除法去定位。

Java

    5.3调优建议

    TradeOff:与CAP注定要缺一角一样,GC优化要在延迟(Latency)、吞吐量(Throughput)、容量(Capacity)三者之间进行权衡。

    最终手段:GC发生问题不是一定要对JVM的GC参数进行调优,大部分情况下是通过GC的情况找出一些业务问题,切记上来就对GC参数进行调整,当然有明确配置错误的场景除外。

    控制变量:控制变量法是在蒙特卡洛(MonteCarlo)方法中用于减少方差的一种技术方法,我们调优的时候尽量也要使用,每次调优过程尽可能只调整一个变量。

    善用搜索:理论上99.99%的GC问题基本都被遇到了,我们要学会使用搜索引擎的高级技巧,重点关注StackOverFlow、Github上的Issue、以及各种论坛博客,先看看其他人是怎么解决的,会让解决问题事半功倍。能看到这篇文章,你的搜索能力基本过关了~

    调优重点:总体上来讲,我们开发的过程中遇到的问题类型也基本都符合正态分布,太简单或太复杂的基本遇到的概率很低,笔者这里将中间最重要的三个场景添加了“*”标识,希望阅读完本文之后可以观察下自己负责的系统,是否存在上述问题。

    GC参数:如果堆、栈确实无法第一时间保留,一定要保留GC日志,这样我们最起码可以看到GCCause,有一个大概的排查方向。关于GC日志相关参数,最基本的-XX:+HeapDumpOnOutOfMemoryError等一些参数就不再提了,笔者建议添加以下参数,可以提高我们分析问题的效率。

Java

    其他建议:上文场景中没有提到,但是对GC性能也有提升的一些建议。

    主动式GC:也有另开生面的做法,通过监控手段监控观测Old区的使用情况,即将到达阈值时将应用服务摘掉流量,手动触发一次MajorGC,减少CMSGC带来的停顿,但随之系统的健壮性也会减少,如非必要不建议引入。

    禁用偏向锁:偏向锁在只有一个线程使用到该锁的时候效率很高,但是在竞争激烈情况会升级成轻量级锁,此时就需要先消除偏向锁,这个过程是STW的。如果每个同步资源都走这个升级过程,开销会非常大,所以在已知并发激烈的前提下,一般会禁用偏向锁-XX:-UseBiasedLocking来提高性能。

    虚拟内存:启动初期有些操作系统(例如Linux)并没有真正分配物理内存给JVM,而是在虚拟内存中分配,使用的时候才会在物理内存中分配内存页,这样也会导致GC时间较长。这种情况可以添加-XX:+AlwaysPreTouch参数,让VM在commit内存时跑个循环来强制保证申请的内存真的commit,避免运行时触发缺页异常。在一些大内存的场景下,有时候能将前几次的GC时间降一个数量级,但是添加这个参数后,启动的过程可能会变慢。

    以上内容由开课吧老师、新宇、湘铭、祥璞提供,更多Java教程尽在开课吧广场Java教程频道。

有用
分享