案例 3: 某财险公司运行时的 Perm 区内存溢出分析
本文最后更新于:2024年7月24日 晚上
一 概述
1.1 客户需求
1.1.1 客户问题描述
客户通过 Dynatrace 发现某台 Jboss 的 JVM内存突然提交,然后垃圾回收。如下图。
1.1.2 客户需求
客户想要了解 Root Cause。
1.2 收集信息概述
客户发现 Jboss 的内存突然彪增,且 JVM 的 GC 时间大幅增长。查看 Dynatrace 发现,当时该及节点已经发生 Perm 区内存溢出。且有 告警 产生(同时应用关键业务错误率大幅增长)(Perm 区内存溢出导致的 OLD 区彪增和 GC 时间彪增,具体分析见下文)。如下图:
二 事故影响范围
查看当时的主机、JVM 及应用业务情况,该问题造成一系列的连锁影响,包括:
- CPU 利用率升高
- 物理内存升高
- JVM heap 区增大
- JVM gc 及挂起时间变长
- Jboss 线程数上升
- 关键业务全部失败
- 关键业务响应变慢
具体如下图:
2.1 主机
2.2 中间件
2.3 应用
三 问题分析及定位
3.1 我定制 2 个仪表图来分析问题
具体如下图。直接 Perm OOM 的原因是:类加载量的大幅增长(因为 Perm 区存放的就是静态类和常量等,而 Perm OOM JDK 默认会做 fullgc,因此导致 gc 及挂起时间增加;因为无法 GC 掉,会导致 Heap 区调整及 CPU 增加、线程数增加)
3.2 为什么加载的类会突然飙增?
我们对上图放大,查看细节。如下图:
类加载数量是在 8:25-8:30 期间大幅增长的。接下来我们需要查看这期间的该 Jboss 具体在做什么业务。
3.3 查看 8:25-8:30 的 Jboss 上的 purepath
purepath 简单理解:所有的事务的分布式方法调用栈及相关信息。(如响应时间、时间细分、线程、LOG、Exception、SQL、Message 等)
如下图,直接可以看出:
- 导致该问题的 root cause 事务:/RuleManager/showCalib1QueryCondition.htm
- 导致该问题的 root casue 代码:c3p0(c3p0 性能有问题。调用 c3p0 前后会有类加载的动作,正是这个情况导致了当时大量的类加载) (下图 forName0 就是类加载的相关方法)
四 总结及优化建议
4.1 问题发生的先后顺序
- 出现大量的 /RuleManager/showCalib1QueryCondition.htm 请求
- 需要加载大量的 C3p0 相关类
- 类加载数量大幅增长
- Perm 区 256M 内存用完
- 触发 JVM full gc
- gc 及挂起时间增加
- 无法 GC 掉 → CPU 增加、线程增加、Heap 区增加、业务失败
4.2 优化建议
4.2.1 中间件(治标不治本)
-
增大 Perm 区大小
-
优化与 Perm 清理有关的参数(如 Perm 满后清理,不执行 full gc 等)
-
为了更方便的定位问题,特别是在没有 Dynatrace 的情况下,建议在生产环境开启 GC 日志。
阿里的 JAVA 专家说过:
其实线上开 GC 日志没关系啦,我们线上就一直开着,对性能不会有那么大影响的。
4.2.2 开发
- 优化 JDBC 相关代码(如果想要优化 c3p0 代码可以看 3.3 的代码逻辑。如果不想优化建议直接不要采用 c3p0 作为 JDBC 框架,选择其他 JDBC 框架)
4.3 最后说一句
其实这个问题一个月以前就已经分析过了,但是由于当时的影响只是 应用关键业务变慢,所以并没有引起重视。但是这次造成的影响就比较大了,直接导致关键业务全部失败以及 Jboss 长时间挂起(即不可用)。
所以,大的生产事故,其实可能都是因为一些细小的,我们认为没关系或者可以忽略的性能问题导致的。
👊 生产无小事!责任大于天!
系列文章
- 8 种 Java 内存溢出之一:Java Heap Space
- 案例 1: 某财险承保系统内存泄漏问题
- 8 种 Java- 内存溢出之二 -GC overhead limit exceeded
- 案例 2: 某寿险公司核心系统 GC 开销超限问题分析
- 8 种 Java- 内存溢出之三 -Permgen space
- 案例 3: 某财险公司运行时的 Perm 区内存溢出分析
- 8 种 Java- 内存溢出之四 -Metaspace
- 8 种 Java- 内存溢出之五 -Unable to create new native thread
- 8 种 Java- 内存溢出六 -Out of swap space?
- 8 种 Java 内存溢出之七 -Requested array size exceeds VM limit
- 8 种 Java 内存溢出之八 -Kill process or sacrifice child