一.前言
在工作中,开发了一个文件上传的接口,然后开发自测的时候没出现问题,自己搞了个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