2024-4-23 群讨论:Java堆空间OutOfMemoryError该怎么办

以下来自本人拉的一个关于 Java 技术的讨论群。关注公众号:hashcon,私信进群拉你

1. 为什么不建议打开 HeapDumpOnOutOfMemoryError?

1.1. 打开 HeapDumpOnOutOfMemoryError,哪些 OutOfMemoryError 会触发 HeapDumpOnOutOfMemoryError?

打开 HeapDumpOnOutOfMemoryError 之后,不是所有的 OutOfMemoryError 都会触发 HeapDumpOnOutOfMemoryError,不同的 OutOfMemoryError 包括(如果对这些异常抛出的原理详情感兴趣,请参考:zhuanlan.zhihu.com/p/265039643 ):

  1. OutOfMemoryError: Java heap spaceOutOfMemoryError: GC overhead limit exceeded:这两个都是 Java 对象堆内存不够了,一个是分配的时候发现剩余空间不足,一个是到达某一界限。这两个都会触发 HeapDumpOnOutOfMemoryError
  2. OutOfMemoryError: unable to create native thread:无法创建新的平台线程,这个不会触发 HeapDumpOnOutOfMemoryError
  3. OutOfMemoryError: Requested array size exceeds VM limit:当申请的数组大小超过堆内存限制,就会抛出这个异常。这个会触发 HeapDumpOnOutOfMemoryError
  4. OutOfMemoryError: Compressed class spaceOutOfMemoryError: Metaspace:这两个都和元空间相关(底层原理说明参考:juejin.cn/post/722587... ),这两个都会触发 HeapDumpOnOutOfMemoryError
  5. OutOfMemoryError: Cannot reserve xxx bytes of direct buffer memory (allocated: xxx, limit: xxx):在 DirectByteBuffer 中,首先向 Bits 类申请额度,Bits 类有一个全局的 totalCapacity 变量,记录着全部 DirectByteBuffer 的总大小,每次申请,都先看看是否超限,可用 -XX:MaxDirectMemorySize 限制。这个不会触发 HeapDumpOnOutOfMemoryError
  6. OutOfMemoryError: map failed:这个是 File MMAP(文件映射内存)时,如果系统内存不足,就会抛出这个异常。这个不会触发 HeapDumpOnOutOfMemoryError

还有一些其他的:

  1. Shenandoah 分配区域位图,内存的时候,触发的 OutOfMemoryError这个会触发 HeapDumpOnOutOfMemoryError
  2. OutOfMemoryError: Native heap allocation failed,这个 Message 可能不同操作系统不一样,但是一般都有 native heap。这个就和 Java 对象堆一般没关系,而是其他块内存无法申请导致的,这些不会触发HeapDumpOnOutOfMemoryError

1.2. 为什么不打开 HeapDumpOnOutOfMemoryError

HeapDumpOnOutOfMemoryError 的原理:

  1. 进入安全点,所有应用线程暂停,针对 HeapDumpOnOutOfMemoryError,单线程(如果是 jcmd jmap 可以多线程)dump 堆为线程个数个文件。退出安全点。
  2. 将上面的多个文件,合并为一个,压缩。

这里的瓶颈主要在于第一步写入,并且,主要瓶颈再磁盘 IO,我们来看下现在云服务的磁盘 IO 标准:

  1. AWS EFS(普通存储):docs.aws.amazon.com/efs/latest/...
  2. AWS EBS(对标 SSD):docs.aws.amazon.com/ebs/latest/...

对于一个 4G 大小的堆内存,如果是 EFS,对标的应该是 100G 以内的磁盘,写入最少也需要大概 4 * 1024 / 300 = 13.65 秒(注意,这个是峰值性能),如果当时峰值性能被用完了,那么需要:4 * 1024 / 15 = 273 秒。如果用 EBS,那么也需要 4 * 1024 / 1000 = 4 秒。注意,这个计算的时间,是应用线程个完全处于安全点(即 Stop-the-world)的时间,还没有还是没考虑一个机器上部署多个容器实例的情况,考虑成本我们也不能堆每个微服务都使用 AWS EBS 这种(对标 SSD)。

所以,建议还是不要打开 HeapDumpOnOutOfMemoryError

2. 不使用 HeapDumpOnOutOfMemoryError 用什么?

2.1. 定位内存泄漏问题靠 JFR

我这边定位 OutOfMemoryError 一般通过 JFR 的 Object Allocation Sample 以及 Old Object Sample 里面的对象去定位,只有这些都定位不出来,才会考虑 Heap Dump。

2.2. 为什么抛出 OutOfMemoryError 的微服务最好下线重启?

因为包括 JDK 的源码在内,都没有在每一个分配内存的代码的地方考虑会出现 OutOfMemoryError,这样会导致代码状态不一致,例如 hashmap 的 rehash,如果里面某行抛出 OutOfMemoryError,前面更新的状态就不对了。还有其他很多库,就不用说了,都很少有 catch Throwable 的,大部分是 catch Exception 的。并且,在每一个分配内存的代码的地方考虑会出现 OutOfMemoryError 也是不现实的,所以为了防止 OutOfMemoryError 带来意想不到的一致性问题,还是下线重启比较好。

2.3. 如何实现抛出 OutOfMemoryError 的微服务下线重启?

一般通过 -XX:OnOutOfMemoryError="/path/to/script.sh"指定脚本,脚本执行:

  1. 微服务的下线
  2. 微服务的重启

针对 spring boot,可以考虑开启允许本地访问 /actuator/shutdown 来关闭微服务(有群友反应抛出 OutOfMemoryError 的时候调用这个会卡死,这是因为 1.2 说的原因,你可能开启了 HeapDumpOnOutOfMemoryError 导致的️),k8s 会自动拉起一个新的。

个人简介:个人业余研究了 AI LLM 微调与 RAG,目前成果是微调了三个模型:

  1. 一个模型是基于 whisper 模型的微调,使用我原来做的精翻的视频按照语句段落切分的片段,并尝试按照方言类别,以及技术类别分别尝试微调的成果。用于视频字幕识别。
  2. 一个模型是基于 Mistral Large 的模型的微调,识别提取视频课件的片段,辅以实际的课件文字进行识别微调。用于识别课件的片段。
  3. 最后一个模型是基于 Claude 3 的模型微调,使用我之前制作的翻译字幕,与 AWS、Go 社区、CNCF 生态里面的官方英文文档以及中文文档作为语料,按照内容段交叉拆分,进行微调,用于字幕翻译。
    目前,准确率已经非常高了。大家如果有想要我制作的视频,欢迎关注留言。
    本人也是开源代码爱好者,贡献过很多项目的源码(Mycat 和 Java JFRUnit 的核心贡献者,贡献过 OpenJDK,Spring,Spring Cloud,Apache Bookkeeper,Apache RocketMQ,Ribbon,Lettuce、 SocketIO、Langchain4j 等项目 ),同时也是深度技术迷,编写过很多硬核的原理分析系列(JVM)。本人也有一个 Java 技术交流群,感兴趣的欢迎关注。
    另外,一如即往的是,全网的所有收益,都会捐赠给希望工程,坚持靠爱与兴趣发电。
相关推荐
努力也学不会java1 小时前
【设计模式】 原型模式
java·设计模式·原型模式
方渐鸿1 小时前
【2024】k8s集群 图文详细 部署安装使用(两万字)
java·运维·容器·kubernetes·k8s·运维开发·持续部署
学亮编程手记1 小时前
K8S v1.33 版本主要新特性介绍
java·容器·kubernetes
Haven-2 小时前
Java-面试八股文-JVM篇
java·jvm·面试
我真的是大笨蛋3 小时前
JVM调优总结
java·jvm·数据库·redis·缓存·性能优化·系统架构
wjs0403 小时前
Git常用的命令
java·git·gitlab
superlls3 小时前
(算法 哈希表)【LeetCode 349】两个数组的交集 思路笔记自留
java·数据结构·算法
田里的水稻3 小时前
C++_队列编码实例,从末端添加对象,同时把头部的对象剔除掉,中的队列长度为设置长度NUM_OBJ
java·c++·算法
ponnylv4 小时前
深入剖析Spring Boot启动流程
java·开发语言·spring boot·spring
前行的小黑炭4 小时前
Android 协程的使用:结合一个环境噪音检查功能的例子来玩玩
android·java·kotlin