线上项目报错OOM常见原因、排查方式、解决方案

概述

OutOfMemoryError(OOM)是 Java 应用程序中常见的问题,通常是由于应用程序占用的内存超过了 JVM 分配的最大内存限制。在 Spring Boot 项目中,OOM 问题可能由多种原因引起。


1. OOM 的常见原因

OOM 通常由以下几种情况引起:

1.1 内存泄漏(Memory Leak)

  • 对象被意外地长时间持有,导致无法被垃圾回收(GC)。

  • 常见场景:

    • 静态集合类(如 static Map)持续增长。

    • 未关闭的资源(如数据库连接、文件流)。

    • 缓存未设置过期时间或大小限制。

1.2 堆内存不足

  • 应用程序需要的内存超过了 JVM 堆内存的最大限制(通过 -Xmx 设置)。

  • 常见场景:

    • 加载大量数据到内存中(如大文件、大查询结果)。

    • 高并发场景下,对象创建速度过快,GC 来不及回收。

1.3 元空间(Metaspace)或永久代(PermGen)溢出

  • 类加载器加载的类过多,导致元空间或永久代内存不足。

  • 常见场景:

    • 动态生成大量类(如使用反射、动态代理)。

    • 未设置元空间大小限制(默认情况下,元空间可以无限增长)。

1.4 线程栈溢出

  • 线程栈空间不足(通过 -Xss 设置)。

  • 常见场景:

    • 递归调用过深。

    • 线程数过多。

1.5 直接内存溢出

  • 使用 NIO 的 DirectByteBuffer 分配的直接内存超出限制。

  • 常见场景:

    • 大量使用堆外内存(如 Netty、文件读写)。


2. 排查 OOM 的步骤

2.1 确认 OOM 的类型

OOM 有不同的类型,可以通过错误日志确认:

  • java.lang.OutOfMemoryError: Java heap space:堆内存不足。

  • java.lang.OutOfMemoryError: Metaspace:元空间内存不足。

  • java.lang.OutOfMemoryError: PermGen space:永久代内存不足(Java 8 之前)。

  • java.lang.OutOfMemoryError: Unable to create new native thread:线程栈溢出。

  • java.lang.OutOfMemoryError: Direct buffer memory:直接内存溢出。

2.2 启用 JVM 参数记录日志

在启动 Spring Boot 应用时,添加以下 JVM 参数,以便在 OOM 发生时生成堆转储文件(Heap Dump)和 GC 日志:

java -Xmx512m -Xms512m \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/path/to/dump \
-XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-XX:+PrintGCTimeStamps \
-Xloggc:/path/to/gc.log \
-jar your-spring-boot-app.jar
  • -XX:+HeapDumpOnOutOfMemoryError:在 OOM 时生成堆转储文件。

  • -XX:HeapDumpPath:指定堆转储文件的保存路径。

  • -XX:+PrintGCDetails 和 -Xloggc:记录 GC 日志,用于分析 GC 行为。

2.3 分析堆转储文件

使用工具(如 Eclipse MAT、VisualVM、JProfiler)分析生成的堆转储文件(.hprof 文件),找出内存占用最多的对象和可能的泄漏点。

  • Eclipse MAT

    • 打开 .hprof 文件。

    • 查看 Histogram,找出占用内存最多的对象。

    • 使用 Leak Suspects 报告,分析可能的内存泄漏。

  • VisualVM

    • 加载 .hprof 文件。

    • 查看对象实例数和占用内存大小。

2.4 分析 GC 日志

使用工具(如 GCViewer、GCEasy)分析 GC 日志,检查是否存在以下问题:

  • Full GC 频率过高。

  • 老年代(Old Generation)内存持续增长。

  • GC 后内存回收效果不佳。

2.5 监控运行时内存

使用监控工具(如 Prometheus + Grafana、Spring Boot Actuator、Arthas)实时监控应用的内存使用情况:

  • 堆内存、元空间、直接内存的使用情况。

  • 线程数、GC 次数和时间。


3. 解决 OOM 的方法

3.1 增加 JVM 内存

如果 OOM 是由于堆内存不足引起的,可以尝试增加 JVM 内存:

java -Xmx2g -Xms2g -jar your-spring-boot-app.jar
  • -Xmx:设置最大堆内存。

  • -Xms:设置初始堆内存。

3.2 优化代码

  • 避免内存泄漏

    • 及时释放资源(如关闭数据库连接、文件流)。

    • 避免在静态集合中存储大量数据。

    • 使用弱引用(WeakReference)或软引用(SoftReference)管理缓存。

  • 减少对象创建

    • 重用对象(如使用对象池)。

    • 避免在循环中创建大量临时对象。

  • 优化数据加载

    • 分页加载大数据集。

    • 使用流式处理(如 Java Stream API)代替一次性加载所有数据。

3.3 调整元空间大小

如果 OOM 是由于元空间不足引起的,可以调整元空间大小:

java -XX:MaxMetaspaceSize=256m -XX:MetaspaceSize=128m -jar your-spring-boot-app.jar

3.4 限制缓存大小

如果使用缓存(如 Ehcache、Guava Cache),确保设置缓存的最大大小和过期时间:

Cache<String, Object> cache = CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(10, TimeUnit.MINUTES).build();

3.5 优化线程池

如果 OOM 是由于线程栈溢出引起的,可以:

  • 减少线程栈大小:

java -Xss256k -jar your-spring-boot-app.jar

限制线程池大小:

ExecutorService executor = Executors.newFixedThreadPool(100);

3.6 优化直接内存使用

如果 OOM 是由于直接内存溢出引起的,可以:

  • 增加直接内存大小:

java -XX:MaxDirectMemorySize=512m -jar your-spring-boot-app.jar

显式释放 DirectByteBuffer

((DirectBuffer) buffer).cleaner().clean();

4. 预防 OOM 的最佳实践

  • 定期监控:使用 APM 工具(如 SkyWalking、Pinpoint)监控应用的内存和 GC 情况。

  • 压力测试:在发布前进行压力测试,模拟高并发场景,检查内存使用情况。

  • 代码审查:定期检查代码,避免常见的内存泄漏问题。

  • 合理配置 JVM 参数:根据应用需求,合理设置堆内存、元空间、线程栈等参数。

通过以上方法,可以有效排查和解决 Spring Boot 应用中的 OOM 问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/71246.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java练习(27)

ps&#xff1a;练习来自力扣 删除排序链表中的重复元素 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 代码来自官方题解 class Solution {public ListNode deleteDuplicates(ListNode head) {//…

Flutter:动态表单(在不确定字段的情况下,生成动态表单)

关于数据模型&#xff1a;模型就是一种规范约束&#xff0c;便于维护管理&#xff0c;在不确定表单内会出现什么数据时&#xff0c;就没有模型一说。 这时就要用到动态表单&#xff08;根据接口返回的字段&#xff0c;生成动态表单&#xff09; 1、观察数据格式&#xff0c;定义…

洛谷P8707 [蓝桥杯 2020 省 AB1] 走方格

#include <iostream> using namespace std; int f[31][31]; int main(){int n,m;scanf("%d%d",&n,&m);f[1][1]1;//边界&#xff1a;f(1,1)1for(int i1;i<n;i)for(int j1;j<m;j)if((i&1||j&1)&&(i!1||j!1))//i,j不均为偶数&#…

ASP.NET Core Web应用(.NET9.0)读取数据库表记录并显示到页面

1.创建ASP.NET Core Web应用 选择.NET9.0框架 安装SqlClient依赖包 2.实现数据库记录读取: 引用数据库操作类命名空间 创建查询记录结构类 查询数据并返回数据集合 3.前端遍历数据并动态生成表格显示 生成结果:

解决 Linux 中搜狗输入法导致系统崩溃的问题【fcitx 】【ibus】

在 Linux 系统中安装搜狗输入法时&#xff0c;有时会遇到一些令人头疼的问题。最近&#xff0c;我在安装搜狗输入法后&#xff0c;系统出现了崩溃的情况。具体表现为输入密码登录后&#xff0c;界面卡死&#xff0c;无法正常进入系统。经过一番排查和分析&#xff0c;我终于找到…

如何做好项目变更管理

项目变更管理是确保项目按时、按预算和按质量要求完成的关键环节之一。有效的项目变更管理包括&#xff1a;变更识别、变更评审、变更批准和变更实施。这些步骤确保项目在面对变化时能够高效应对&#xff0c;避免资源浪费、时间延误和预算超支。其中&#xff0c;变更评审和变更…

HarmonyOS-ArkTS基础快速入门

目录 ArkTS 快速入门 ArkTS 快速入门 如图&#xff0c;index.etc里面的内容&#xff08;图中框住的大长方形区域&#xff09;会渲染到预览区中&#xff0c;而console.log(xx,xxx)用于内容的打印&#xff0c;需要在日志中查看打印的内容

【设计模式】【结构型模式】桥接模式(Bridge)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f3b5; 当你的天空突…

深度学习(1)-简单神经网络示例

我们来看一个神经网络的具体实例&#xff1a;使用Python的Keras库来学习手写数字分类。在这个例子中&#xff0c;我们要解决的问题是&#xff0c;将手写数字的灰度图像&#xff08;28像素28像素&#xff09;划分到10个类别中&#xff08;从0到9&#xff09;​。我们将使用MNIST…

深入探索 C++17 中的 std::hypot:从二维到三维的欧几里得距离计算

文章目录 1. std::hypot 的起源与背景2. 三维空间中的 std::hypot3. 为什么需要 std::hypot 而不是手动计算&#xff1f;4. 使用 std::hypot 的示例4.1 二维空间中的应用4.2 三维空间中的应用4.3 处理浮点数溢出问题 5. std::hypot 的性能与精度6. 实际应用场景6.1 计算机图形学…

面基Spring Boot项目中实用注解一

在Spring Boot项目中&#xff0c;实用注解根据功能可以分为多个类别。以下是常见的注解分类、示例说明及对比分析&#xff1a; 1. 核心配置注解 SpringBootApplication 作用&#xff1a;标记主启动类&#xff0c;组合了Configuration、EnableAutoConfiguration和ComponentScan…

【每日论文】Latent Radiance Fields with 3D-aware 2D Representations

下载论文或阅读原文&#xff0c;请点击&#xff1a;每日论文 摘要 中文 潜在3D重建技术在赋予3D语义理解和3D生成能力方面展现出巨大的潜力&#xff0c;它通过将2D特征提炼到3D空间来实现。然而&#xff0c;现有的方法在2D特征空间和3D表示之间的领域差距问题上挣扎&#xff…

CPP集群聊天服务器开发实践(七):Github上传项目

github链接&#xff1a;GitHub - arduino-ctrl/ClusterServer: 基于jsonmuduomysqlnginxredis的集群服务器与客户端通信源码 步骤如下&#xff1a; 1. github新建代码仓库&#xff0c;复制url 2. git clone https://github.com/arduino-ctrl/ClusterServer.git 3. 将项目文件…

作业。。。。。

顺序表按元素删除 参数&#xff1a;删除元素&#xff0c;顺序表 1.调用元素查找的函数 4.根据下表删除 delete_sub(list,sub); //删除元素 void delete_element(int element, Sqlist *list) …

二、从0开始卷出一个新项目之瑞萨RZT2M双核架构通信和工程构建

一、概述 RZT2M双核架构是同构多核&#xff0c;但双核针对不同应用 扩展多核架构和通信知识可参见嵌入式科普(30)一文看懂嵌入式MCU/MPU多核架构与通信 二、参考资料 用户手册&#xff1a;RZ/T2M Group Users Manual: Hardware R52内核手册&#xff1a;arm_cortex_r52_proc…

【HF设计模式】07-适配器模式 外观模式

声明&#xff1a;仅为个人学习总结&#xff0c;还请批判性查看&#xff0c;如有不同观点&#xff0c;欢迎交流。 摘要 《Head First设计模式》第7章笔记&#xff1a;结合示例应用和代码&#xff0c;介绍适配器模式和外观模式&#xff0c;包括遇到的问题、采用的解决方案、遵循…

RDMA 高性能通信技术原理

目录 文章目录 目录DMA 与 RDMARDMA 特性和优势大带宽低延时 RDMA 协议栈标准RDMA 运行原理通信通路通信模型通信方式内存注册QP 建链常规流程双向控制 Send-Receive API 流程单向数据 Write API 流程单向数据 Read API 流程 RDMA Verbs API 编程基础网络连通性RDMA C/S 程序 D…

HCIA项目实践(网络)---NAT地址转化技术

十三 NAT网络地址转换技术 13.1 什么是NAT NAT&#xff08;Network Address Translation&#xff09;地址转换技术&#xff0c;是一种将内部网络的私有 IP 地址转换为外部网络的公有 IP 地址的技术。其主要作用是实现多个内部网络设备通过一个公有 IP 地址访问外部网络&#x…

【JAVA工程师从0开始学AI】,第四步:闭包与高阶函数——用Python的“魔法函数“重构Java思维

副标题&#xff1a;当严谨的Java遇上"七十二变"的Python函数式编程 历经变量战争、语法迷雾、函数对决&#xff0c;此刻我们将踏入Python最迷人的领域——函数式编程。当Java工程师还在用接口和匿名类实现回调时&#xff0c;Python的闭包已化身"智能机器人"…

el-tree选中数据重组成树

vueelement-ui 实现el-tree选择重新生成一个已选中的值组成新的数据树&#xff0c;效果如下 <template><div class"flex"><el-tree class"tree-row" :data"list" ref"tree" :props"{children: children, label: …