案例 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 问题发生的先后顺序

  1. 出现大量的 /RuleManager/showCalib1QueryCondition.htm 请求
  2. 需要加载大量的 C3p0 相关类
  3. 类加载数量大幅增长
  4. Perm 区 256M 内存用完
  5. 触发 JVM full gc
  6. gc 及挂起时间增加
  7. 无法 GC 掉 → CPU 增加、线程增加、Heap 区增加、业务失败

4.2 优化建议

4.2.1 中间件(治标不治本)

  1. 增大 Perm 区大小

  2. 优化与 Perm 清理有关的参数(如 Perm 满后清理,不执行 full gc 等)

  3. 为了更方便的定位问题,特别是在没有 Dynatrace 的情况下,建议在生产环境开启 GC 日志。

    阿里的 JAVA 专家说过:

    其实线上开 GC 日志没关系啦,我们线上就一直开着,对性能不会有那么大影响的。

4.2.2 开发

  • 优化 JDBC 相关代码(如果想要优化 c3p0 代码可以看 3.3 的代码逻辑。如果不想优化建议直接不要采用 c3p0 作为 JDBC 框架,选择其他 JDBC 框架)

4.3 最后说一句

其实这个问题一个月以前就已经分析过了,但是由于当时的影响只是 应用关键业务变慢,所以并没有引起重视。但是这次造成的影响就比较大了,直接导致关键业务全部失败以及 Jboss 长时间挂起(即不可用)。

所以,大的生产事故,其实可能都是因为一些细小的,我们认为没关系或者可以忽略的性能问题导致的。

👊 生产无小事!责任大于天!

系列文章

  1. 8 种 Java 内存溢出之一:Java Heap Space
  2. 案例 1: 某财险承保系统内存泄漏问题
  3. 8 种 Java- 内存溢出之二 -GC overhead limit exceeded
  4. 案例 2: 某寿险公司核心系统 GC 开销超限问题分析
  5. 8 种 Java- 内存溢出之三 -Permgen space
  6. 案例 3: 某财险公司运行时的 Perm 区内存溢出分析
  7. 8 种 Java- 内存溢出之四 -Metaspace
  8. 8 种 Java- 内存溢出之五 -Unable to create new native thread
  9. 8 种 Java- 内存溢出六 -Out of swap space?
  10. 8 种 Java 内存溢出之七 -Requested array size exceeds VM limit
  11. 8 种 Java 内存溢出之八 -Kill process or sacrifice child

案例 3: 某财险公司运行时的 Perm 区内存溢出分析
https://ewhisper.cn/posts/56989/
作者
东风微鸣
发布于
2017年11月2日
许可协议