ByteArrayOutputStream导致OOM问题源码解析

一.前言

在工作中,开发了一个文件上传的接口,然后开发自测的时候没出现问题,自己搞了个40M的视频上传,需求要求的是最大可接收1G,于是找了个海绵宝宝第678集1.2G在本地服务上传;
于是测试完成上传到线上(这里我反思不应该在本地服务测试应该发到测试环境,当时偷懒);
结果线上上传一个400M的视频就导致OOM;
我说不应该呀,我本地咋不会出现OOM,是不是线上内存太小了,上阿里云K8s上一看配置参数:内存才600M;继续疑惑那400M的视频也不至于导致内存溢出吧;

二.解决问题

于是读代码:
工具类代码:

public static ByteArrayOutputStream inputToByteArrayOutPut(InputStream input) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        try {
            while ((len = input.read(buffer)) > -1) {
                baos.write(buffer, 0, len);
            }
            baos.flush();
        } catch (IOException e) {
            throw new Exception("Illegal flow.");
        } finally {
            try {
                input.close();
            } catch (IOException e) {
                log.error("file stream shutdown failed.");
            }
        }
        return baos;
    }

这个方法就是 inputToByteArrayOutPut 将一个输入流(InputStream)的内容读入到一个字节数组输出流(ByteArrayOutputStream)中,然后返回输出流;平平无奇
继续深入底层 读源码: ByteArrayOutputStream的.write方法

public synchronized void write(byte b[], int off, int len) {
        if ((off < 0) || (off > b.length) || (len < 0) ||
            ((off + len) - b.length > 0)) {
            throw new IndexOutOfBoundsException();
        }
        ensureCapacity(count + len);
        System.arraycopy(b, off, buf, count, len);
        count += len;
}

这个 write 方法是一个用于将字节数组的一部分写入缓冲区的同步方法。它首先进行边界检查,确保参数合法,然后调用 ensureCapacity 方法确保缓冲区有足够的空间,最后使用 System.arraycopy 将字节数组的内容复制到缓冲区中,然后更新计数器 count;
继续-read源码: ensureCapacity()方法

private void ensureCapacity(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - buf.length > 0)
            grow(minCapacity);
}

从这里看如果复制的容量大于缓冲区容量就调用扩容方法
grow(minCapacity)

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = buf.length;//获取当前数组的容量 作为 旧容量 (oldCapacity);
        int newCapacity = oldCapacity << 1; //将旧容量左移一位(即乘以2),得到新容量
        if (newCapacity - minCapacity < 0)  //检查新容量是否小于最小需求容量 (minCapacity
            newCapacity = minCapacity;//如果新容量小于最小需求容量,则将新容量设置为最小需求容量,以确保至少能容纳 minCapacity 个元素。
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        buf = Arrays.copyOf(buf, newCapacity);
    }

问题在这里就出现了

int newCapacity = oldCapacity << 1; //将旧容量左移一位(即乘以2),得到新容量

每次扩容数组都是以旧容量的两倍扩容

这就会导致可能视频在300M~400M复制的时候就触发了扩容,导致OOM

而我本地是设置了2048M的内存,所以之前测试都没有问题

解决方法:1.根据视频大小提前设置缓冲池大小 2.扩大线上内存至2048M

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ByteArrayOutputStream导致内存OOM的原因是它在内存中持有一个缓冲区,用于存储写入的数据。当写入的数据量过大时,缓冲区可能会溢出,导致内存不足的错误。在引用的代码中,通过将文件的数据读取到ByteArrayOutputStream中,如果文件大小超过了堆内存的限制,就会导致内存OOM。 在这段代码中,文件的数据被一次性读取到了一个大小为8MB的字节数组中,然后通过ByteArrayOutputStream的write方法将字节数组写入到内存中。如果文件过大,每次读取的数据量太大,就会导致内存OOM。 解决这个问题的方法是通过分段读取文件,每次只读取一部分数据,然后写入到ByteArrayOutputStream中。这样可以避免一次性读取大量数据导致内存OOM问题。可以使用循环的方式,每次读取一定大小的数据,直到读取完整个文件为止。 另外,在处理大文件时,可以考虑使用BufferedInputStream来提高读取文件的效率。BufferedInputStream可以减少磁盘IO次数,对性能有一定的提升。 总结起来,使用ByteArrayOutputStream时要注意内存的限制,避免一次性读取大量数据导致内存OOM。可以通过分段读取文件的方式来解决这个问题,同时可以考虑使用BufferedInputStream来提高读取文件的效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [一次OOM分析-ByteArrayOutPutStream#write引起](https://blog.csdn.net/thewindkee/article/details/102703279)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值