【问题标题】:FileChannel.transferFrom fails for larger files with Out of memory Error对于较大的文件,FileChannel.transferFrom 因内存不足错误而失败
【发布时间】:2015-01-22 12:08:18
【问题描述】:

FileChannel.transferFrom(source, 0, source.size()) 在尝试复制大小约为 2GB 的文件时会出现以下 OutOfMemory 异常。我了解由于文件较大而导致的内存问题。我们可以通过在循环中以小块处理文件来解决它吗?

01-22 17:27:03.365: W/System.err(28538): java.io.IOException: mmap failed: ENOMEM (Out of memory)
01-22 17:27:03.375: W/System.err(28538):    at java.nio.MemoryBlock.mmap(MemoryBlock.java:119)
01-22 17:27:03.375: W/System.err(28538):    at java.nio.FileChannelImpl.map(FileChannelImpl.java:249)
01-22 17:27:03.380: W/System.err(28538):    at java.nio.FileChannelImpl.transferFrom(FileChannelImpl.java:381)
01-22 17:27:03.380: W/System.err(28538):    at com.druva.inSync.util.InSyncIOUtils.copyFile(InSyncIOUtils.java:123)
01-22 17:27:03.380: W/System.err(28538):    at com.druva.inSync.AsyncTasks.ProcessUploadTask.getFileItemForFile(ProcessUploadTask.java:102)
01-22 17:27:03.380: W/System.err(28538):    at com.druva.inSync.AsyncTasks.ProcessUploadTask.processUploads(ProcessUploadTask.java:124)
01-22 17:27:03.380: W/System.err(28538):    at com.druva.inSync.AsyncTasks.ProcessUploadTask.doInBackground(ProcessUploadTask.java:53)
01-22 17:27:03.380: W/System.err(28538):    at com.druva.inSync.AsyncTasks.ProcessUploadTask.doInBackground(ProcessUploadTask.java:1)
01-22 17:27:03.380: W/System.err(28538):    at android.os.AsyncTask$2.call(AsyncTask.java:287)
01-22 17:27:03.380: W/System.err(28538):    at java.util.concurrent.FutureTask.run(FutureTask.java:234)
01-22 17:27:03.380: W/System.err(28538):    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
01-22 17:27:03.380: W/System.err(28538):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
01-22 17:27:03.380: W/System.err(28538):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
01-22 17:27:03.380: W/System.err(28538):    at java.lang.Thread.run(Thread.java:841)
01-22 17:27:03.385: W/System.err(28538): Caused by: libcore.io.ErrnoException: mmap failed: ENOMEM (Out of memory)

编辑:

我尝试了以下代码:

    for (long n = 0, s = source.size() >> 1; n < s;) {
     Log.d("copy file", "inside for loop " + destination.size()); 
     long c = destination.transferFrom(source, n, s - n); 
     n += c; 
     Log.d("copy file", "results: c=" + c + ", n=" + n); 
    } 

但它只复制文件的前半部分...

【问题讨论】:

  • 为了让您知道为什么您的代码不起作用,这里有一个解释:source.size()>>1 本质上是 source.size()/2。您的循环基本上说“尝试将一半大小的块传输到目标。存储实际传输到c的字节数”。然后它将 c 添加到 n - 如果 transferFrom 有效,则将 n 设置为源大小的一半。然后你的 for 循环检查 n
  • 继续我之前的解释:请注意,如果您的示例代码中的 tranferFrom 碰巧失败,它将返回零。然后,您将向 n 添加零,并且您的 for 循环将检查 n

标签: android filechannel


【解决方案1】:

我在使用 FileChannel.transferFrom 时遇到了同样的问题,但我什至偶尔会看到小于 512MB 的文件的 ENOMEM 错误。

这是我最终用来以更小块传输文件的代码:

  // Transfer file in 256MB blocks
  final long blockSize = Math.min(268435456, sourceChannel.size());
  long position = 0;
  while (destinationChannel.transferFrom(sourceChannel, position, blockSize) > 0) {
    position += blockSize;
  }

transferFrom 方法返回它成功复制的字节数。如果它返回一个大于零的数字,它复制了整个块,所以尝试复制另一个块。当它不复制任何东西时,你就完成了。

【讨论】:

  • 这对我有用,但即使使用largeHeap=true,我也必须转移到较小的块中。我用了 50MB,效果很好,谢谢!
猜你喜欢
  • 2013-03-14
  • 1970-01-01
  • 2014-01-06
  • 2021-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-28
  • 2016-06-02
相关资源
最近更新 更多