JVM实战(33)——内存溢出之内存使用率过高

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析

阶段4、深入jdk其余源码解析

阶段5、深入jvm源码解析

一、简介

本章,我们将讲解一个已经稳定运行的系统的内存溢出问题,该内存溢出问题的元凶是类加载器,我们先来看下系统的背景。

1.1 系统背景

这个系统已经在线上稳定运行了一段时间,部署在Tomcat中启动。突然有一天收到告警,显示许多访问该系统的调用请求出现假死现象,但是过了一会儿又可以了。经过排查发现,每隔一段时间,系统就会出现假死。

一般来说,系统出现假死,接口无法调用,就是系统的资源不足以处理新的请求,所以我们先通过top命令排查下机器的CPU和内存使用情况。

  • 如果这个服务大量使用了内存,导致频繁Full GC(这个问题我们之前的章节介绍过),从而引发STW,接口调用就会出现假死现象。
  • 如果机器的CPU负载太高,比如某个进程耗尽了CPU资源,那么正常服务就始终无法得到CPU去执行,这也可能导致假死。

我们通过top命令发现,系统本身对CPU消耗非常少,也就1%,但是却耗费了50%的总内存。要知道,机器配置是4核8G,我们给JVM进程分配的总内存最多也就是4-5G,系统消耗了4-5G的内存,说明JVM中的Java堆内存几乎被占满。

二、问题分析

2.1 内存使用率过高

我们先分析下,进程占用内存过高会导致什么,一般会发生三种情况:

  1. 频繁Full GC,GC带来的Stop the World导致程序假死;
  2. 内存占用过多,引起内存溢出;
  3. 内存使用率过高,导致程序进程因为申请内存不足,直接被操作系统给干掉。

我们先用jstat分析下GC情况,发现确实经常发生GC,但是每次GC耗时也就几百毫秒,程序也没有因此出现假死现象。

我们再排查是否发生了OOM,经过日志分析,程序并没有抛出任何OOM异常。那只有第三种情况了,可能是程序进程被OS杀掉,然后由于自启脚本又重新启动了,但是在这段时间内,程序无法被访问,就出现了假死的现象。

2.2 JVM参数不合理

我们通过MAT分析dump出的内存快照,发现有一大堆的ClassLoader占用超过了50%的内存。最后,根据MAT层层抽丝剥茧,发现是写这个代码的童鞋搞了个自定义的类加载器,但是代码中无限制的创建了大量的自定义类加载器,重复加载了大量数据,结果一下子把内存耗尽了,导致程序进程被OS杀掉。

三、系统优化

优化方式很明显,就是修改有问题的代码点,避免重复创建自定义的类加载器,避免重复加载大量数据到内存中。

四、总结

本章介绍的案例和之前有点区别,程序其实并没有发生OOM异常,但又确实是因为内存占用过多而被OS杀死。这个案例告诉大家,无论如何优化分析,它们背后的原理都是一套东西,掌握分析问题的思路才是最重要的。

相关推荐
Mr. zhihao10 小时前
死锁排查实战:JVM 唯一会“自动报案“的问题(场景 B5)
java·jvm·gc
Mr. zhihao15 小时前
OOM 排查实战(大结局):从老年代爬坡到 VisualVM 引用链实锤(场景 B4)
jvm·gc·oom·mat·visulvm
H_老邪16 小时前
JVM 遇到的问题-1.0
jvm
liangbo720 小时前
JVM规范第 4 章:class 文件格式
java·jvm
夕除21 小时前
redis--008
java·jvm·数据库
liangbo721 小时前
JVM规范第 1章:从一段历史到「JVM 并不认识 Java」
java·jvm
月华路1 天前
G1 GC 对数组与大对象(Humongous)的处理
java·jvm·算法
月华路2 天前
G1 新生代对象晋升老年代:实现机制与 GC 日志
java·jvm·算法
Shadow(⊙o⊙)2 天前
C++进阶知识5.0
jvm
一木 之林2 天前
五、C++新特性、关键字与编译原理
java·jvm·算法