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杀死。这个案例告诉大家,无论如何优化分析,它们背后的原理都是一套东西,掌握分析问题的思路才是最重要的。

相关推荐
Java成神之路-3 小时前
深入理解 G1 Mixed GC:触发条件、完整流程与高频误区
jvm·g1垃圾回收算法
认真的小羽❅4 小时前
ThreadLocal 在虚拟线程间要小心
java·开发语言·jvm
今天的砖头有点烫手啊5 小时前
Java 8 Stream 原理拆解:惰性求值与并行流陷阱
java·jvm
用户094248568036 小时前
第37章:JVM极端场景调优与生产故障复盘方法论
java·jvm
南极企鹅8 小时前
生产环境gc文件分析
java·jvm·算法
欣栀♡18 小时前
JVM 频繁 Full GC 先看日志括号里的 cause,jstat 的 FGC 在 JDK 8 上会骗人
java·jvm
czt_java18 小时前
JVM 核心基础|运行时数据区、类加载机制、GC 分代回收
jvm
Web极客码21 小时前
Python Ollama 本地 Agent 入门与订单查询实践
开发语言·jvm·python
y = xⁿ21 小时前
一文掌握JVM
jvm
顺风尿一寸1 天前
从 FileOutputStream 到 int fd:一次 JNI 字段读写全链路源码剖析
linux·jvm