【问题标题】:File download - Out Of Memory(OOM)文件下载 - 内存不足(OOM)
【发布时间】:2014-03-03 04:51:21
【问题描述】:

我在某些设备上下载文件时遇到了一些问题,我收到 OOM 错误。这是我用来下载大文件的代码:

/**
 * The size of the chunks that an file is split when writing to server.<br />
 * 1024 * 1024 -> 1mb
 */
private static final int CHUNK_SIZE = 1024 * 1024;

File output = new File(sdCardPath, fileName);
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(output);
} catch (FileNotFoundException e) {
e.printStackTrace();
}

int offset = 0;

// compute the number of chunks of 1 mb for downloading the file
// by parts
int parts = tmpFileSize / CHUNK_SIZE;
ByteString readingfile = null;
long progressUpdate = 0;

for (int partsCounter = 0; partsCounter < parts + 1; partsCounter++) {
    try {
        readingfile = serviceApi
                .readFile(
                        session,
                        filehandle, offset, CHUNK_SIZE);

        byte[] bytesRead = readingfile.toByteArray();
        int numberOfBytesReaded = bytesRead.length;
        offset = offset + numberOfBytesReaded;
        progress.publish(""
                + (int) ((progressUpdate * 100) / tmpFileSize));
        progressUpdate += numberOfBytesReaded;
        fileOutputStream.write(bytesRead, 0,
                numberOfBytesReaded);

    } catch (IOException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

try {
    if (null != fileOutputStream) {
        fileOutputStream.flush();
        fileOutputStream.close();
    }
} catch (IOException e) {
    e.printStackTrace();
}

如果我在这里做错了什么,有人可以告诉我吗?谢谢。


基于@Ari 答案的解决方案我已经更新了代码。现在它被优化为仅使用 1mb (我不知道这是否是将进程分成块的最佳方法,但现在似乎有所改进,并且不会产生 OOM)。我将尝试通过检测我可以使用多少堆内存来进一步优化它,但我不确定我能否实现这一点。直到这似乎是最好的选择。 再次感谢@Ari。

【问题讨论】:

  • 你下载的图片?
  • 它可以是任何类型的文件。

标签: android file out-of-memory large-files


【解决方案1】:

您有许多不需要的缓冲区。

  1. byte[] br = readingfile.toByteArray(); 你用来获取numberOfBytesReaded
  2. 然后你又得到了数组:inputStream = ... readingfile.toByteArray()); 并将其复制到第三个缓冲区
  3. byte data[] = new byte[bufferSize];

尝试只对所有这些操作使用一个。

一般建议是在不再需要对象(和数组)的指针时将它们设置为 NULL

我会使用这样的代码:

for (int partsCounter = 0; partsCounter < parts + 1; partsCounter++) {
    readingfile = serviceApi.readFile(session, filehandle, offset,
            (int) bufferSize);
    byte[] br = readingfile.toByteArray();
    int numberOfBytesReaded = br.length;
    offset = offset + numberOfBytesReaded;

    try {
            progress.publish(""
                    + (int) ((progressUpdate * 100) / tmpFileSize));
            progressUpdate += numberOfBytesReaded;
            fileOutputStream.write(br, 0, numberOfBytesReaded);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

【讨论】:

  • 这不是让我 OOM 更快吗?或者你认为因为我已经将文件分成块,所以每次迭代都会覆盖 fileOutputStream ?
  • @IonutNegru 我更新了代码。我认为,您的代码将使用 4MB 内存(2 个缓冲区、1 个输入流和 1 个输出)。我的只需要 1MB + 128KB。
  • @IonutNegru 我认为fileOutputStream 不需要自己的内存,所以你的代码使用了 3MB,我的使用了 1MB。
  • 这是一种可怕的做事方式。如果您不需要字节数据,则永远不要将其完全读入内存。而是打开一个输入和输出流并一次复制(例如)8kb(Apache commons-io 项目为此提供了现成的工具)。 ByteStream.toByteArray() 创建整个文件数据的完全无用的副本,并且原始副本被丢弃(当循环结束时)。那个 readFile(...) 方法不适合这项工作。
  • @IonutNegru 更大的块意味着:可能更快的下载,更高的连接错误风险(特别是对于慢速连接;发送一个块需要更多时间),支持的设备更少。测试不同的块大小并选择最适合你的块,但我不会使用大于 256kB 的块
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-14
  • 2014-07-05
  • 1970-01-01
  • 2012-05-23
  • 1970-01-01
相关资源
最近更新 更多