Java使用AWS SDK上传S3时Multipart Upload提速及内存溢出问题咨询
问题1解答
- 分片上传本身具备提速能力,但提速效果受上行带宽上限限制:如果单连接上传已经占满本地全部上行带宽,再多并发分片也无法提升总上传速度,就会出现和普通上传耗时持平的情况。
- 推荐使用分片上传的核心原因不是提速,而是以下几点:
- 支持断点续传:网络中断后仅需要重传失败的分片,不需要重新上传整个文件,大文件场景下稳定性提升极其明显
- 支持更大的单文件上传:普通上传最大只能上传5GB的单个文件,分片上传最大支持5TB的单个文件
- 失败重试成本极低:单个分片上传失败的重试开销远小于整个大文件重新上传的开销
问题2解答
- 你遇到的内存溢出问题和分片上传机制本身无关,是代码实现和SDK默认逻辑共同导致的:
- 代码首先把整个base64编码的大文件一次性全量解码到堆内存的byte数组中,相当于一开始就把整个文件加载到内存,这是OOM的核心诱因之一
- 没有给
ObjectMetadata设置ContentLength参数,AWS SDK无法提前获知文件总大小,加上传入的是不可回退的ByteArrayInputStream,SDK为了切分分片,会把整个输入流的内容再全量缓存到内存中,相当于同一份文件在内存里存了两份,直接导致内存占用翻倍
- 分片上传默认会并发上传全部分片:TransferManager默认的并发数和当前设备CPU核心数挂钩,每个并发上传的分片都会占用对应的内存缓冲区,并发数越高,瞬时内存占用越大。
代码修复建议
你可以按照以下优先级调整代码解决问题:
- 给
ObjectMetadata显式设置解码后的文件长度,避免SDK缓存整个流计算大小:
metadata.setContentLength(bI.length);
- 调整TransferManager配置,降低分片并发数、调大分片大小,降低内存占用:
tm = TransferManagerBuilder.standard() .withS3Client(s3Client) .withMultipartUploadThreshold((long) (50* 1024 * 1024)) .withMinimumUploadPartSize((long) (10 * 1024 * 1024)) // 单分片10MB,可按需调整 .withExecutorFactory(() -> Executors.newFixedThreadPool(3)) // 并发数调整为3,可按需调整 .build();
- 优化base64解码逻辑,不要一次性把整个base64字符串转成byte数组,改用流式解码处理,避免一开始就加载全量文件到内存。
内容的提问来源于stack exchange,提问作者Alain Del Rosario
相关产品推荐
相关产品推荐

