【问题标题】:Can't download a 100mb file on Android with LibGDX Net无法使用 LibGDX Net 在 Android 上下载 100mb 文件
【发布时间】:2017-01-24 17:45:08
【问题描述】:

我有一个简单的代码,它向我的外部服务器发送 Http 请求以下载大小为 100mb 的 .txt 文件。较小的文件(如 40mb)可以正常工作,但较大的文件存在一些问题。让我给你看一些代码:

Net.HttpRequest request = new Net.HttpRequest(Net.HttpMethods.GET);
    request.setTimeOut(2500);
    String assetsUrl = "http://111.111.111.111/100mb.txt";
    request.setUrl(assetsUrl);


    // Send the request, listen for the response
    // Asynchronously
    Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener() {
        @Override
        public void handleHttpResponse (Net.HttpResponse httpResponse) {


            InputStream is = httpResponse.getResultAsStream();
            OutputStream os = Gdx.files.local("100mb.txt").write(false);

            byte[] bytes = new byte[1024];
            int count = -1;

            try {

                while ((count = is.read(bytes, 0, bytes.length)) != -1) {
                    os.write(bytes, 0, count);
                }

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


        }

还有一些代码可以显示进度,但这并不重要。 问题是文件的大小是 100mb,但是 Android 在下载时神奇地分配了 400mb+ 的 RAM 并出现错误:

等待阻塞的 GC Alloc

WaitForGcToComplete 因 Alloc 原因阻塞 12.906 毫秒

启动阻塞 GC Alloc

启动阻塞 GC Alloc

暂停所有线程耗时:35.332ms

Alloc 部分并发标记扫描 GC 释放 214(21KB) AllocSpace 对象,1(200MB) LOS 对象,6% 空闲,216MB/232MB,暂停 1.076ms 总计 130.115ms

暂停所有线程耗时:205.400ms

后台粘性并发标记扫描 GC 释放 364(10KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 416MB/416MB, paused 10.448ms 总计 304.325ms

启动阻塞 GC Alloc 启动阻塞 GC Alloc

Alloc 部分并发标记扫描 GC 释放 113(3KB) AllocSpace objects, 0(0B) LOS objects, 3% free, 416MB/432MB, paused 290us total 17.611ms

启动阻塞 GC Alloc

Alloc 粘性并发标记扫描 GC 释放 31(912B) AllocSpace objects, 0(0B) LOS objects, 3% free, 416MB/432MB, paused 268us total 6.474毫秒

启动阻塞 GC Alloc

Alloc 并发标记扫描 GC 释放了 43(13KB) AllocSpace 对象, 0(0B) 个 LOS 对象,3% 空闲,415MB/431MB,暂停 268us 共 15.008ms

强制为 300MB 分配收集 SoftReferences

启动阻塞 GC Alloc

Alloc 并发标记扫描 GC 释放了 42(1256B) 个 AllocSpace 对象, 0(0B) 个 LOS 对象,3% 空闲,415MB/431MB,暂停 286us 共 12.426ms

抛出 OutOfMemoryError "未能分配 314572860 字节 分配 16770608 个空闲字节和 96MB 直到 OOM"

当我运行下载过程时,我可以在我的设备上看到我分配了 1 GB(按系统)和 600 mb 空闲空间,而应用程序使用 20-30 mb。几秒钟后,我的应用程序开始分配越来越多的内存,我可以看到,它使用了 400mb+ 并且崩溃发生在你在日志中看到的最大值时。

也许我不明白,但它不应该只分配所需的 100mb 的 RAM 来存储数据块吗? 我几乎 100% 确定我的应用程序没有泄漏 - 下载过程正在消耗内存(肯定只调用一次)。

【问题讨论】:

    标签: android memory download libgdx garbage-collection


    【解决方案1】:

    你所做的一切看起来都不奇怪,所以我看了一下 LibGDX,尤其是 getResultAsString;最终你会回到 com.badlogic.gdx.utils.StreamUtils.copyStreamToString

        public static String copyStreamToString (InputStream input, int estimatedSize, String charset) throws IOException {
            InputStreamReader reader = charset == null ? new InputStreamReader(input) : new InputStreamReader(input, charset);
            StringWriter writer = new StringWriter(Math.max(0, estimatedSize));
            char[] buffer = new char[DEFAULT_BUFFER_SIZE];
            int charsRead;
            while ((charsRead = reader.read(buffer)) != -1) {
                writer.write(buffer, 0, charsRead);
            }
            return writer.toString();
        }
    

    这看起来并不可怕,但我的猜测是 StringWriter 出了问题,导致内部重复重新分配数组。看着this answer 似乎证实了我的怀疑。

    StringWriter 在内部写入 StringBuffer。 StringBuffer 基本上是一个 char 数组的包装器。那阵有一定的容量。当该容量不足时,StringBuffer 将分配一个新的更大的 char 数组并复制前一个的内容。最后在 StringWriter 上调用 toString(),它会再次将 char 数组的内容复制到结果 String 的 char 数组中。

    因此,简而言之,您可能应该找到另一种下载文件的方法。 This question 可能有一些更强大的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-10
      • 2019-04-25
      • 2014-05-21
      • 1970-01-01
      • 1970-01-01
      • 2016-09-11
      相关资源
      最近更新 更多