上传100MB视频文件到服务器时出现OutOfMemoryError该如何修复?
问题根本原因
- 核心问题大概率是
contentLength()方法实现错误:如果该方法没有直接调用file.length()获取本地文件长度,而是手动读取文件内容计算大小,那么每次循环调用都会重新加载一次文件内容,循环几百次就会把内存占满。小文件因为加载耗时短、占用内存低,不容易触发阈值,大文件就会直接OOM。 - 其次是Okio Buffer使用逻辑存在隐患:部分Okio版本中调用
sink.write(buffer, readCount)不会自动清理Buffer中已写出的内容,会导致Buffer持续累积读取到的文件数据,最终内存溢出。 - 额外隐患:原代码中try-with-resources仅管理了Buffer的生命周期,Source的关闭逻辑放在finally里,出现极端异常时可能出现资源泄漏,加重内存负担。
修复方案
首先确认contentLength()直接返回文件原生长度,不要做读取文件内容的操作,示例实现:
@Override public long contentLength() { return file.length(); }
然后修改writeTo逻辑,提前获取文件长度、优化Buffer管理,修复后代码如下:
@Override public void writeTo(@NotNull BufferedSink sink) throws IOException { // 提前一次性获取文件总长度,避免循环内重复调用 long originalFileLength = contentLength(); long remaining = originalFileLength; // 用try-with-resources统一管理Source和Buffer,自动释放资源 try (Source source = Okio.source(file); Buffer buffer = new Buffer()) { for (long readCount; (readCount = source.read(buffer, DEFAULT_BUFFER_SIZE)) != -1; ) { if (mMediaUploadService.isRetryShown()) break; sink.write(buffer, readCount); // 主动清理Buffer中已写出的内容,避免内存累积 buffer.clear(); sink.flush(); remaining -= readCount; remainingBytes = remaining; LogMessage.d(TAG, "source size: " + originalFileLength + " remaining bytes: " + remainingBytes); uploadTaskCompletedListener.onUploadProgressChanged(readCount, originalFileLength); } firstTimeCall[0] = true; } catch (Exception e) { LogMessage.e(TAG, "write to exception: = " + e.getMessage()); } }
如果要进一步简化逻辑,可以直接使用Okio自带的BufferedSource实现,不需要手动操作Buffer:
@Override public void writeTo(@NotNull BufferedSink sink) throws IOException { long originalFileLength = contentLength(); try (BufferedSource source = Okio.buffer(Okio.source(file))) { long readCount; byte[] tempBuffer = new byte[DEFAULT_BUFFER_SIZE]; while ((readCount = source.read(tempBuffer)) != -1) { if (mMediaUploadService.isRetryShown()) break; sink.write(tempBuffer, 0, (int) readCount); sink.flush(); uploadTaskCompletedListener.onUploadProgressChanged(readCount, originalFileLength); } firstTimeCall[0] = true; } catch (Exception e) { LogMessage.e(TAG, "write to exception: = " + e.getMessage()); } }
内容的提问来源于stack exchange,提问作者Mohan K
相关产品推荐
相关产品推荐

