记一次生产OOM:Requested array size exceeds VM limit

2023年 7月 15日 62.6k 0

前言

本文记录了一次线上OOM问题的解决过程,从乌龙分析到精准分析,最终解决了问题。文章中,详细介绍了是怎么发生了乌龙, 最后又是怎么准确定位到问题及解决这个问题的过程,最后还讨论了一些常见的OOM问题,并提供了一些解决方案。

事情回顾

  • 某天晚上19:50,服务器监控突然报警, 报警显示OOM,
  • 经排查是在对一个25万人的明细数据JSON字符串进行压缩的地方
    image.png
    一看是OOM, 然后经排查又是在25万人的场景,联想到系统之前处理最大的字符串是20万人的人员明,自然而然就定位为是内存不足,需要升级配置
  • 通知PE进行机器配置升级, 从原先的4c16g升到8c32g。
  • 这个时候自认为轻松解决了该问题。事与愿违,升配后没过多久服务器又开始OOM报警, 但按理不应该再OOM了, 内存已经升了一倍, 按经验系统之前16g能处理20万的, 现在32g处理25万完全没有问题, 于是仔细看了一下OOM的具体错误, 是“Requested array size exceeds VM limit”,

    而他的意思是“就说明想要创建的[数组长度]超过限制。”

    分析

    这个错误是由JVM中的本地代码抛出的. 在真正为数组分配内存之前, JVM会执行一项检查: 要分配的数据结构在该平台是否可以寻址(addressable). 当然, 这个错误比你所想的还要少见得多

    因为Java使用 int 类型作为数组的下标(index, 索引)。在Java中, int类型的最大值为 2^31 – 1 = 2,147,483,647。大多数平台的限制都约等于这个值

    只要定义一个数组定义大小为Integer.MAX_VALUE即可重现

    image.png

    image.png

    定位

    于是从新看了一下出问题的代码

    image.png
    这里compressFileObject.getFileContent()是一个String对象, 在String转为byte[]数据的时候抛出了这个错, 让我们再看一下String.getBytes()是怎么处理的, 可以发现核心逻辑是encode这个方法, 而抛错的代码行是 byte[] ba = new byte[en]; 而这个en就是string本身的长度, 所以推测就是25万的人员明细String太长了, 导致getByte的时候超过了JVM的数组长度限制

    image.png

    image.png

    解决

    定位到了原因, 是因为String转Byte[]时, String的长度太长, 既然整个String太长了, 那是否可以分割, 分批进行String转Byte[], 因为看String的getByte[], 他的实现逻辑其实一个字符一个字符转的, 既然是这样,我分批处理后再合并和直接处理的效果是一样的, 说干就干, 动手改代码

    image.png

    并且我把压缩后的bytes保存到文件,加压后比较原来的文件和解压的文件,md5是一致的,看起来没有问题。

    于是进行hotfix验证发布流程, 最后解决了OOM的问题

    总结

    其实造成OOM的类型有很多,并不是OOM就是内存不足, 我们还是需要根据具体的错误信息进行具体的分析, 否则很容易造成误判

    我也列了几个OOM的具体类型

    java.lang.OutOfMemoryError: Java heap space

    这个异常的原因为内存泄漏或者内存溢出。内存溢出的时候,需要调整JVM参数-Xmx配置,调大堆空间,如果是内存泄漏,就需要找出泄漏的代码。

    java.lang.OutOfMemoryError: GC overhead limit exceeded

    这种异常的原因是垃圾收集器GC效率很低,jvm花费超过 98%的 CPU 时间来进行一次 GC,但是回收的内存却少于 2%的堆空间大小,并且GC连续超过5次都这样

    通过增加参数-XX:-UseGCOverheadLimit可以避免这个异常,但其实是自己骗自己,还是需要实际去定位解决问题。

    java.lang.OutOfMemoryError: Requested array size exceeds VM limit

    这个异常很容易理解,请求分配的数组大小超过jvm限制,出现这种情况的原因有2个: 请求分配的数组太大,导致jvm空间不足 请求的数组大于等于Integer.MAX_INT - 1

    java.lang.OutOfMemoryError: MetaSpace

    这个异常是元空间不足,解决办法是加大元空间大小,配置参数MaxMetaSpaceSize,在启动引用时加入参数: -XX:MaxMetaspaceSize=2m

    java.lang.OutOfMemoryError: Request size bytes for reason. Out of swap space

    这个异常是操作系统的swap空间不够引起的。我们知道jvm分配的最大内存由Xmx等一些参数指定,如果jvm需要的总内存超出了宿主机可以分配的最大的物理内存,就会用到swap space,如果swap space不足,jvm内存分配就会失败,从而抛出这个异常。这个异常的定位比较复杂,有可能是宿主机上面的其他进程耗用内存太多导致。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论