生产环境 OOM 与 GC 问题的处理思路
有一定 Java 工作经验的朋友们,免不了要遇到过,或者处理过 OOM 和 GC 问题。OOM 和 GC 问题也是面试时,经常被面试官问题的问题。分享一下多年积累的一些小经验,共同进步。
0x01:未雨绸缪
部署到生产环境的应用,无论是 C/S 结构,还是 B/S 结构的应用服务。肯定有基于 Shell 脚本编写的启动脚本。C/S 结构的应用服务的 Shell 脚本一般是公司内部开发人员编写的;以下一个 C/S 结构应用服务的简单启动脚本。
java -Xms1024m -Xmx1024m -XX:PermSize=256m \
-XX:MaxPermSize=512m -XX:-HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=./ -XX:+PrintGCDetails -Xloggc:./gc.log -jar \
plugins/org.eclipse.equinox.launcher_1.0.201.R35x_v20090715.jar -clean -refresh &
而 B/S 结构的一般是应用服务器的启动 Shell 脚本。例如,应用服务器 Apache Tomcat bin 目录下的 startup.sh。
而 Apache Tomcat 的启动 Shell 脚本并没有配置发生 OOM 时,打印 JVM 内存快照的JVM参数和打印 GC 日志的JVM参数。所以生成环境的 Tomcat 服务一般需要进行 JVM 参数优化。
怎么对线上的 OOM 和 GC 问题进行未雨绸缪呢?那就是认为自己部署的任何服务都是会发生 OOM 和 GC 问题的。在启动脚本里加上相应的参数,防止真的出现 OOM 和 GC 问题时,无证可查。
打印 OOM 快照配置:
-
-XX:-HeapDumpOnOutOfMemoryError :当堆内存空间溢出时输出堆的内存快照
-
-XX:HeapDumpPath :指定输入的目录
也就是说当发生 OutOfMemoryError 错误时,才能触发 -XX:HeapDumpOnOutOfMemoryError 输出到 -XX:HeapDumpPath 指定的目录。
打印 GC 日志:
-
-XX:+PrintGCDetails:打印 GC 日志详细信息
-
-Xloggc:GC 日志输入的目录
0x02:线上分析
有时并不一定是要宕机了才去分析 OOM 和 GC 问题。目前大型的系统服务都是配备了监控系统,一旦发现服务处于不健康运行的状态,就会触发预警。给运维人员发送邮件、短信等,这时就可以对触发预警的服务进行问题分析了。这时对这些正在提供生成服务的应用服务进行分析就需要额外小心,稍不留神就造成更严重的生产事故,给公司带来严重的损失,同时也给自己的考核带来不利影响。
对于向上分析一般使用 JDK 提供的各种运维工具,找到问题的所在。
-
监视工具:jps、jstat、jstatd、jmc
-
故障排除工具:jcmd、jinfo、jhat、jmap、jsadebugd、jstack
官网:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/
jps (JavaVirtual Machine Process Status Tool): 虚拟机进程状况工具
命令格式:jps [options] [hostid]
-q : 抑制类名的输出,JAR文件名和传递给main方法的参数,仅生成本地JVM标识符列表。
-m: 显示传递给该main方法的参数。输出可能是null嵌入式JVM。
-l : 显示应用程序main类的完整包名或应用程序的JAR文件的完整路径名。
-v : 显示传递给JVM的参数。
-V : 抑制类名的输出,JAR文件名和传递给main方法的参数,仅生成本地JVM标识符的列表。
-Joption : 传递option给JVM,其中的选项是optionsJava应用程序启动器的参考页面中描述的选项之一。
例如,-J-Xms48m将启动内存设置为48 MB。
- jstat (Java Virtual Machine (JVM) statistics):监视Java虚拟机(JVM)统计信息
命令格式:jstat [ option vmid [interval[s|ms] [count] ]
Interval: 间隔时间 count:次数
class:显示类加载器行为的统计信息。
compiler:显示有关Java HotSpot VM即时编译器行为的统计信息。
gc:显示垃圾回收堆行为的统计信息。
gccapacity:显示有关世代及其对应空间容量的统计数据。
gccause:显示有关垃圾回收统计信息(相同-gcutil)的摘要,其中包含最后和当前(适用时)垃圾收集事件的原因。
gcnew:显示新一代行为的统计信息。
gcnewcapacity:显示有关新一代及其相应空间大小的统计信息。
gcold:显示旧版本和Metaspace统计信息的统计信息。
gcoldcapacity:显示有关旧一代大小的统计信息。
gcmetacapacity:显示有关元空间大小的统计信息。
gcutil:显示有关垃圾收集统计信息的摘要。
printcompilation:显示Java HotSpot VM编译方法统计信息。
- jinfo (Configuration Info for Java):生成配置信息
命令格式:jinfo [ option ] pid
-flag 名称 : 打印指定命令行标志的名称和值。
-flag [+ | - ]名称 : 启用或禁用指定的布尔命令行标志。
-flag name = value : 将指定的命令行标志设置为指定的值。
-flags : 打印传递给JVM的命令行标志。
-sysprops : 将Java系统属性打印为名。
- jmap (Memory Map for Java):内存映射工具 [ 生成堆转储快照 ]
命令格式:jinfo [ option ] vmid
-dump:[live,] format = b,file = filename :以hprof二进制格式转储Java堆filename。live子选项说明是否之dump出存活的对象。
-finalizerinfo : 打印有关正在等待最终确定的对象的信息(linux)。
-heap :显示java堆详细信息,如使用哪种回收器、参数配置、分代状况等(linux)。
-histo [:live] : 显示堆中对象统计信息,包括类、实例数量、合计容量。
-clstats : 打印Java堆的类加载器智能统计。对于每个类加载器,它的名称,它的活动程度,地址,父类加载器以及它加载的类的数量和大小。
-F : -dump或 -histo选项不响应时,该选项强制生成dump快照(不支持live)。
- jhat (JVM Heap Analysis Tool):虚拟机堆转储快照分析工具
命令格式:jhat [ options ] 堆转储文件
- jstack (Stack Trace for Java):Java堆栈跟踪工具
命令格式:jstack [ options ] pid
-F : jstack[ -l] pid不响应时强制堆栈转储。
-l : 打印有关锁的其他信息,例如,java.util.concurrent 所拥有的同步器列表。
-m : 打印混合模式堆栈跟踪,其中包含 Java 和本机 C/C ++ 框架。
这么多监视工具和故障排除工具中,常用的是 jps、jstat、jstack 和 jmap
0x03:线下分析
通过未雨绸缪和线上分析还没法肉眼看出一些问题的端倪来的话,为了不影响生产。就需要把这两步收集的 JVM 内存快照,拿到线下来分析。JDK 和一些第三方工具,提供了非常好用的可视化工具来分析JVM 内存快照。主要有 JDK 提供的 jconsole、VisualVM;第三方提供的有Eclipse Memory Analyzer(免费)、JProfiler(商业)。常用可以化工具可以参考该文章 [ Java进行内存泄露 GC 分析都有哪些常用好用的工具 ]