android - 无法使用 LibGDX Net 在 Android 上下载 100mb 文件

标签 android memory download libgdx garbage-collection

我有一个简单的代码,它向我的外部服务器发送一个 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,并出现错误:

Waiting for a blocking GC Alloc

WaitForGcToComplete blocked for 12.906ms for cause Alloc

Starting a blocking GC Alloc

Starting a blocking GC Alloc

Suspending all threads took: 35.332ms

Alloc partial concurrent mark sweep GC freed 214(21KB) AllocSpace objects, 1(200MB) LOS objects, 6% free, 216MB/232MB, paused 1.076ms total 130.115ms

Suspending all threads took: 205.400ms

Background sticky concurrent mark sweep GC freed 364(10KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 416MB/416MB, paused 10.448ms total 304.325ms

Starting a blocking GC Alloc Starting a blocking GC Alloc

Alloc partial concurrent mark sweep GC freed 113(3KB) AllocSpace objects, 0(0B) LOS objects, 3% free, 416MB/432MB, paused 290us total 17.611ms

Starting a blocking GC Alloc

Alloc sticky concurrent mark sweep GC freed 31(912B) AllocSpace objects, 0(0B) LOS objects, 3% free, 416MB/432MB, paused 268us total 6.474ms

Starting a blocking GC Alloc

Alloc concurrent mark sweep GC freed 43(13KB) AllocSpace objects, 0(0B) LOS objects, 3% free, 415MB/431MB, paused 268us total 15.008ms

Forcing collection of SoftReferences for 300MB allocation

Starting a blocking GC Alloc

Alloc concurrent mark sweep GC freed 42(1256B) AllocSpace objects, 0(0B) LOS objects, 3% free, 415MB/431MB, paused 286us total 12.426ms

Throwing OutOfMemoryError "Failed to allocate a 314572860 byte allocation with 16770608 free bytes and 96MB until OOM"

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

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

最佳答案

你所做的一切看起来都不奇怪,所以我查看了 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 writes to a StringBuffer internally. A StringBuffer is basically a wrapper round a char array. That array has a certain capacity. When that capacity is insufficient, StringBuffer will allocate a new larger char array and copy the contents of the previous one. At the end you call toString() on the StringWriter, which will again copy the contents of the char array into the char array of the resulting String.

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

关于android - 无法使用 LibGDX Net 在 Android 上下载 100mb 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39532451/

相关文章:

Node.js:如何通过我的服务器将远程文件流式传输给用户?

android - 在 Android 应用程序中使用谷歌翻译库?

Android RTSP/UDP "RTSP/1.0 461 Unsupported transport"通过蜂窝网络 (4G)

android - 房间数据库 onConflict = OnConflictStrategy.REPLACE 不起作用

java - 实例方法是如何存储的

c# - 使用 download.aspx 时加载图像缓慢

java - “打开项目”遇到错误

c - C 编程中覆盖寄存器 int 失败

javascript - 对象的属性名称如何存储在 Javascript 中?

android - 如何列出我的应用程序以供下载文件?