• JVM 调优案例分析


    JVM 调优案例分析

    一、脚本引擎导致metaspace fullgc及内存溢出

    现象:系统日常运行正常突然发生fullgc甚至内存溢出,重启后恢复正常但是过了几天又会突然发生频繁fullgc触发告警,告警信息如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eVO3DB9R-1660206496513)(media/16602051997600/16602053054267.jpg)]

    首先发生fullgc,我们需要定位fullgc发生在jvm哪个区域?我们可以通过cat也可以通过gc日志查询发生的区域,目前有两种方式可以快速定位到gc区域:

    1、Cat的Problem中可以直接查询error的详细信息(时间比较久了找不到对应的cat截图了)

    2、通过gclog查询(大部分系统没有添加gc日志打印),常见配置如下

    # 必备
    -XX:+PrintGCDetails
    -XX:+PrintGCDateStamps
    -XX:+PrintTenuringDistribution
    -XX:+PrintHeapAtGC
    -XX:+PrintReferenceGC
    -XX:+PrintGCApplicationStoppedTime
      
    # 可选
    -XX:+PrintSafepointStatistics
    -XX:PrintSafepointStatisticsCount=1
      
    # GC日志输出的文件路径
    -Xloggc:/path/to/gc-%t.log
    # 开启日志文件分割
    -XX:+UseGCLogFileRotation
    # 最多分割几个文件,超过之后从头文件开始写
    -XX:NumberOfGCLogFiles=14
    # 每个文件上限大小,超过就触发分割
    -XX:GCLogFileSize=100M
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jnGIEYAR-1660206496514)(media/16602051997600/16602053359132.jpg)]

    通过以上分析可以看出fullgc发生再metaspace区,按照以往的经验,Metaspace在系统稳定运行一段时间后占用空间应该比较稳定才对。我们知道Metaspace主要存储类的元数据,比如我们加载了一个类,出现频繁fullgc的时候说明在不断的加载类,194621k->165280k发现也卸载了不少类,说明在频繁地生成大量”一次性“的类,于是分析dump文件:

    具体步骤:
    1.申请当前机器堡垒机权限
    2.登录堡垒机
    3.查看当前Java进程 ps -ef|grep java
    4.将此机器下线(防止进行dump文件时对其他业务造成影响)
    5.进行应用dump操作
    sudo -u tuhu jmap -dump:format=b,file=/tmp/heapDump.hprof [pid]
    6.修改dump文件权限 sudo chmod 644 /tmp/heapDump.hprof
    7.对dump文件进行压缩操作(dump的文件很大 一般不压缩的话 很难下载的)
    cd /tmp 进入到 /tmp 目录
    tar -zcvf dump.tar.gz ./heapDump.hprof 进行压缩,得到压缩文件 dump.tar.gz
    8.在文件视图里面 下载dump文件
    9.下载到本地解压: tar -xzvf dump.tar.gz //解压tar.gz

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9LRohgUp-1660206496515)(media/16602051997600/16602054492164.jpg)]

    通过visualvm分析(mat没有发现问题)发现频繁创建了很多Script类,看reference是Lhs类,于是分析代码Lhs的compile方法:

    /**
     * 使用aviator编译原始表达式
     */
    public void compile() {
        RuleAssert.requireNotEmptyWhenCompiling(expression,
                RuleErrorEnum.RULE_COMPILE_INVALID_EXPRESSION);
        compiledExpression = AviatorEvaluator.compile(this.expression);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    经过查询aviator官网文档分析发现确实存在内存溢出的问题,在编译表达式的时候每次都产生新的匿名类,这些类会占用 JVM 方法区(Perm 或者 metaspace)
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8N8W6RiV-1660206496515)(media/16602051997600/16602054933042.jpg)]

    解决方案:问题修复比较简单编译的时候使用缓存模式,如:AviatorEvaluator.compile(this.expression, true),重新发布后metaspace稳定

    二、kafka消费不均衡导致cpu负载高

    现象:广告投放服务突然出现很多cpu负载过高告警,尤其是在发布的时候告警频繁,告警信息如下:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xcbETHyY-1660206496527)(media/16602051997600/16602055390537.jpg)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ebqv0rgt-1660206496529)(media/16602051997600/16602055567528.jpg)]

    出现负载过高首先我们要明白什么场景下回导致cpu负载过高?才能快速定位到问题原因,个人总结cpu占用高主要有以下几点:

    1、存在线程无限循环的占用cpu

    2、Young GC频繁导致CPU占用率飙升

    3、创建了大量线程的应用程序导致cpu飙升

    4、存在密集型计算线程

    这里推荐一个比较好用的工具 arthas(阿尔萨斯),提供了很多命令如:thread,jvm,memory等,于是我们通过thread分析线程情况:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W71sI9x5-1660206496530)(media/16602051997600/16602055746693.jpg)]

    通过线程信息可以快速定位到是mkt_ta_track_topic.sub消费线程占用了大量的cpu资源,通过简单的分析定位到问题,主要因为两个问题:

    1、ta埋点(消息量很大)测试目前是做了简单的判断打印日志,没有做其他业务逻辑并且使用了多线程消费,消费速度很快相当于空循环

    2、mkt_ta_track_topic当时是申请了8个分区,目前只有5台机器意味着有部分机器可能消费多个分区,此时占用cpu更严重;另外服务发布的时候也会导致kafka分区重分配也会出现上面说的负载过高问题

    解决方案:

    1、调整线程池多线程消费为单线程消费(后面做业务正常处理再做对应的调整)

    2、申请扩展机器添加3台服务,为什么添加机器不是调整分区?服务负载本来就很高,借此机会扩容,扩容之后资源利用率依然有75.97%

    三:FullGC 停顿时间过长导致上游调用服务超时

    背景:新接手了一个服务,上游反馈偶尔调用超时,和上游确认超时发生时间点和具体超时时间,(超时时间为 1000 ms)。

    查看当时系统运行情况,发现正在发生 FullGC ,且停顿时间超过 1000 ms。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xv11crqE-1660206496532)(media/16602051997600/16602058416431.jpg)]

    要对 FullGC 停顿时间太长调优,我们就得先知道影响 FullGc 停顿时间太长的原因有哪些,通常来说,有以下几种:

    1. 堆内存过大
    2. 使用了非低延迟的垃圾收集器(GC)
    3. FGC 期间系统在做其他操作,如 swap 交换空间
    4. 堆内存碎片化严重
    5. 显示System.gc调用

    经排查发现属于第二种情况,JDK 使用为 jdk8 ,默认垃圾收集器为 Parallel Scanenge 收集器,该收集器主打吞吐量,不关心停顿时间。想要低延迟,我们应该使用 CMS 收集器。CMS 是一款以获取最短回收停顿时间为目标的收集器。

    解决方案:
    使用 CMS:

    -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly


    关于更多 JVM 参数调优,可以参数文章:http://t.csdn.cn/vLjsc

  • 相关阅读:
    AMSR/ADEOS-II L1A Raw Observation Counts V003地球表面和大气微波辐射的详细观测数据
    【智能优化算法 】基于适应度相关优化器求解单目标优化问题附matlab代码
    前端使用 Konva 实现可视化设计器(10)- 对齐线
    nginx配置奇怪的问题
    【launch启动文件播放数据包】
    网络编程 lesson6 服务器模型和网络超时检测
    TinyShell(CSAPP实验)
    IntelliJ IDEA 快捷键 Windows 版本
    网络安全——
    sql索引为什么不能用二分查找?
  • 原文地址:https://blog.csdn.net/itguangit/article/details/126288100