(1) 案例问题

  • 一次大促活动开始,导致系统的 CPU 使用率飙升,几乎陷入卡死,无法处理任何请求;重启后,一段时间后又会出现 CPU 使用率飙升,系统继续卡死!

(2) 优化思路

  • 初步排查 CPU 负载过高原因:
    • 两个常见原因:
      • 系统里创建了大量的线程,过多的线程同时并发运行导致机器 CPU 负载过高;
      • JVM 频繁的执行 Full GC,Full GC 是非常耗费 CPU 资源的,系统可能时不时的卡死;
    • 通过 jstat 观察,JVM 的 Full GC 频率极为频繁,几乎每分钟执行一次,一次耗时几百毫秒,导致 CPU 负载很高;
  • 初步排查频繁 Full GC 的问题:
    • 三个常见原因:
      • 内存分配不合理,导致对象频繁进入老年代;
      • 内存泄漏,内存里驻留了大量的不可回收的存活对象,塞满了老年代,导致稍微有一些对象进入老年代就会引发 Full GC;
      • 永久代里的类太多了,触发 Full GC;
      • 代码里执行了 System.gc() 显式触发 GC;
    • 通过 jstat 观察,发现并不存在内存分配不合理,对象频繁进入老年代的问题,而且永久代的内存使用也很正常;
    • 通过 jstat 观察,发现老年代一直驻留了大量的对象,几乎都快塞满了,很有可能是内存泄漏的原因;
  • 基于 MAT 进行内存泄漏分析:

  • 问题原因:
    • 经过开发人员的代码排查,发现是在系统里做了一个 JVM 本地的缓存,把很多数据都加载到内存里去缓存起来,然后提供查询服务的时候直接从本地内存里走,但是因为没有限制本地缓存大小,并且没有使用 LRU 之类算法定期淘汰一些缓存里的数据,导致缓存在内存里的对象越来越多,进而造成内存泄漏;
  • 解决方案:
    • 使用类似 EHCache 之类的缓存框架,它会固定缓存多少个对象,定期淘汰删除掉一些不怎么访问的缓存,一边于新的数据可以进入缓存中。