JAVA 上传文件到对象存储出现OOM
status
Published
type
Post
slug
s3-upload-out-of-memory-error
date
Nov 3, 2022
tags
Java
S3
summary
本文讨论了在Java应用程序中将文件上传到对象存储时出现的内存不足错误。当使用流式上传到S3时,如果直接调用S3客户端的putObject方法,会出现内存不足的风险,因为数据会全部缓存在内存中。解决方法是设置好对象的内容大小,即设置Content Length,以避免内存耗尽的错误。
问题
项目中有一文件服务,负责整个应用系统的文件上传与下载,其后对接了一个兼容 Amazon-S3-API 的对象存储。其中有一个功能是从从程序运行所在 Pod 读取文件 PVC 中文件并上传到对象存储中,此功能实现时直接调用了原有的上传逻辑,且上线后运行正常。在上次发版更新后发现上传的文件在执行后并没有出现在对象存储中,于是开始排查。上传失败的文件大小为 800 MB,日志错误信息显示如下:
日志中可以很明显看到
java.lang.OutOfMemoryError: Java heap space
即程序出现了 OOM 错误初步判断是文件载入内存,内存不够造成的。遂尝试增加了Pod资源分配,调整了JVM堆内存大小。但测试问题还是存在,
此时我注意到每次上传时日志中还有这么一行
No content length specified for stream data. Stream contents will be buffered in memory and could result in out of memory errors.
意思是 Content Length 未设置,流数据将全部载入内存,会造成内存耗尽(OOM)。
查看文件服务上传部分实现,发现底层上传工具库中确实未设置 Content Length
ObjectMetadata objectMetadata = new ObjectMetadata(); objectMetadata.setContentType(file.getFileType()); this.s3.putObject(realBucketName, realFileKey, inputStream, objectMetadata);
那么就加上这个设置
objectMetadata.setContentLength(inputStream.available());
测试通过,不再 OOM,这次就是被原有的上传工具库缺少的这个配置给坑了。
总结
S3 使用流式上传时,如果直接调用 s3 client 的 putObject 方法,会有 Out Of Memory 的风险(数据会全部缓存在内存中),故需设置好对象内容大小。
String content = "Object Content"; ObjectMetadata meta = new ObjectMetadata(); // 设置对象大小 meta.setContentLength(content.length()); client.putObject(bucketName, objectKey, new ByteArrayInputStr eam(content.getBytes()), meta);