美文网首页
JVM故障发现排除

JVM故障发现排除

作者: zhanghTK | 来源:发表于2018-07-01 18:07 被阅读0次

    title: JVM故障发现排除
    date: 2017-12-08 21:20:40
    tags:

    • Java
    • JVM
      categories: JVM

    最近做了服务器迁移之后,系统运行过程中出现了几次发现不稳定的情况。这次的经历又回想起之前几次碰到类似的问题,类似的问题往往需要能快速排查定位、处理。但相关类似问题又不是经常能够碰到,每次出现问题都是手忙脚乱的查资料,今天根据《深入理解 Java 虚拟机》和自己简单的经验做一下总结,方便日后使用。

    工具

    目前可用的 JVM 监控工具还是蛮多的,这里只列出实际操作中我使用的几个。其中有命令行工具也有第可视化工具,从使用的便捷性上讲,可视化的工具无疑是更好的。但生产环境处于安全,性能的考虑往往不开放远程连接,这时还是得用命令行工具处理。

    命令行工具

    • jps:JVM 进程状况工具,主要作用是查看 LVMID,-v 参数可以输出 JMV 启动参数

    • jstat:JVM 统计信息监控工具,主要是查看 GC 相关信息:

      • -gc:监视 Java 堆状况
      • -gccapacity:监视 Java 堆状况,最大,最小空间
      • -gcutil:监视 Java 堆状况,已使用百分比

      输出列含义见文末

    • jinfo:Java 配置信息工具,查看设置 JVM 启动参数

      • 查看:-flag < name >:输出指定名称参数值,作为 jps -v 补充
      • 设置:
        • -flag [+|-] < name >:设置指定 JVM 参数的布尔值
        • -flag < name > = < value >:设置指定 JVM 参数的值
    • jmap:Java 内存映像工具,主要参数:

      • -dump:生成快照,例如:-dump:format=b,file=< filename.bin > < pid >
      • -heap:显示 Java 堆详细信息
      • -histo:像是堆中对象统计信息
      • -permstat:以 ClassLoader 为统计口径显示永久代内存状况
    • jstack:Java 堆栈跟踪工具,用于生成 JVM 当前线程快照,-l 参数显示关于加锁信息,-F 参数强制 dump

    可视化工具

    • VisualVM:多合一故障处理工具,这个几乎涵盖了我用到上面命令行的所有功能
    • MemoryAnalyzerTool:用于分析 dump 堆文件,对比 VisualVM 功能单一,但是提供了报表功能可以协助分析问题

    问题排查一般思路

    目前我在实际开发过程中碰到的 JVM 问题主要可以分类几类:内存溢出、系统运行缓慢、频繁 FullGC

    内存溢出

    这种场景是影响最恶劣,但也是最容易排查的。通常的错误就能告诉说明溢出区域:

    • outOfMemoryError :年老代内存不足
    • outOfMemoryError:PermGen Space:永久代内存不足
    • outOfMemoryError:GC overhead limit exceed:垃圾回收时间占用系统运行时间的98%或以上

    生产环境碰到这类情况为了确保系统可用,可以先使用 jstat 查看 Java 堆的空间使用情况确定到底是哪部分溢出,其最大可用空间是多少,然后直接扩大该区域空间即可。重启时建议加上 -XX:+HeapDumpOnOutMemoryError 作为启动参数,在下次溢出可以获得快照文件。

    上面的做法作为临时解决方案可以解决一般性的问题,但没有系统的快照无法深入分析问题产生的原因,如果想分析问题根源需要在重启前使用 jmap 生成快照。获得快照文件后可以在本地使用可视化工具分析。使用 MemoryAnalyzer 打开快照文件就能获得一个分析报表,里面列出了可能出现泄露的地方。目前我碰到的内存溢出问题一般从这个分析报表里面就可以确定了,如果不能确定就需要根据加载的类,类的实例,引用关系进一步分析了。

    我目前碰到的都是 Java 堆的溢出,以上的思路基本可以解决。但除此还有其他的内存溢出需要注意:

    • Direct Memory
    • 线程堆栈:StackOverflowError,OutOfMemoryError:unable to create new native thread
    • Socket 缓冲区:IOException:Too many open files

    系统运行缓慢

    系统运行缓慢可能出现的原因就比较多了,通常就是找到导致系统缓慢的具体代码段,然后修复。一般化的解决思路是从系统到应用,从应用到线程。

    具体来说:首先使用系统监控工具(例如 top,vmstat)查看当前系统运行状况,确认哪个应用的资源占用过大,是否是 Java 应用的问题;其次使用 jps 获得具体应用的 LVMID,根据 LVMID 查看应用的具体运行状况,如线程情况,系统信息等。还可以根据系统工具 pidstat 进一步查看线程的运行信息来辅助确定问题。以上基本就可以确定问题。

    频繁 FullGC

    频繁 FullGC 需要视情况而定,这里只讨论 FullGC 频繁,但又没有触发 OOM 的排查。这种情况下 dump 快照,直接分析大对象比较靠谱。

    本地应用

    如果是本地应用或者可以远程访问的应用排查起来就更方便了,直接使用 VisualVM 连接上去,从系统到线程的一切信息都了如指掌,还可以直接运行 GC,dump 快照等。


    • 关于工具使用的一些参考:

      JVM性能调优监控工具jps、jstack、jmap、jhat、jstat、hprof使用详解

      MAT - Memory Analyzer Tool 使用进阶

    • jstat 输出列含义:

      S0C:年轻代中第一个survivor的容量 (字节)

      S1C:年轻代中第二个survivor的容量 (字节)

      S0U:年轻代中第一个survivor目前已使用空间 (字节)

      S1U:年轻代中第二个survivor目前已使用空间 (字节)

      EC:年轻代中Eden的容量 (字节)

      EU:年轻代中Eden目前已使用空间 (字节)

      OC:Old代的容量 (字节)

      OU:Old代目前已使用空间 (字节)

      PC:Perm(持久代)的容量 (字节)

      PU:Perm(持久代)目前已使用空间 (字节)

      YGC:从应用程序启动到采样时年轻代中gc次数

      YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)

      FGC:从应用程序启动到采样时old代(全gc)gc次数

      FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)

      GCT:从应用程序启动到采样时gc用的总时间(s)

      NGCMN:年轻代中初始化(最小)的大小 (字节)

      NGCMX:年轻代的最大容量 (字节)

      NGC:年轻代中当前的容量 (字节)

      OGCMN:old代中初始化(最小)的大小 (字节)

      OGCMX:old代的最大容量 (字节)

      OGC:old代当前新生成的容量 (字节)

      PGCMN:perm代中初始化(最小)的大小 (字节)

      PGCMX:perm代的最大容量 (字节)

      PGC:perm代当前新生成的容量 (字节)

      S0:年轻代中第一个survivor已使用的占当前容量百分比

      S1:年轻代中第二个survivor已使用的占当前容量百分比

      E:年轻代中Eden已使用的占当前容量百分比

      O:old代已使用的占当前容量百分比

      P:perm代已使用的占当前容量百分比

      S0CMX:年轻代中第一个survivor的最大容量 (字节)

      S1CMX :年轻代中第二个survivor的最大容量 (字节)

      ECMX:年轻代中Eden的最大容量 (字节)

      DSS:当前需要survivor的容量 (字节)(Eden区已满)

      TT: 持有次数限制

      MTT : 最大持有次数限制


    20171208 补充

    目前系统接入了 APM(Application Performance Management) 对整个系统的运行进行监控。监控内容包括但不限 JVM 相关内容,非常值得参考。
    APM 传送问

    相关文章

      网友评论

          本文标题:JVM故障发现排除

          本文链接:https://www.haomeiwen.com/subject/gndseftx.html