将大json加载到firefox时占用大量内存

时间:2014-04-27 08:59:53

标签: javascript jquery ajax json firefox

我们正在尝试在Firefox 28上使用html5和indexeddb构建基于客户端的应用程序。大型数据首次加载到firefox,首次使用的是AJAX请求的json格式。每个json resposnse大约2MB(gzip),数据将存储到indexeddb IDBWrapper

随着响应,firefox中的内存使用量迅速增加。当我们运行大约12个响应时,firefox的内存将增长到大约1GB(总共3GB),有时它会导致AJAX错误。成功加载后,内存将直接降至500MB或更低。

似乎在整个加载/存储过程中,firefox没有机会进行GC,我们尝试在每个响应之间使用setTimeout,但似乎无能为力。

我的问题是,有没有其他方法可以在加载大json时减少firefox的内存使用量?

编辑: 代码是这样的:

$.ajax({
    url : URL['getData'],
    dataType : "json",
    data : {
        startDate : _startDate,
        endDate : _endDate,
        format : "json"
    },
    async : true,
    success : function(_data) {
        _data = null;
                // store to indexeddb
        _callback();
    },
    error : function() {
        setCurrentDataLabel('Error', 0);
    }
});

我将sotre的部分删除到indexeddb以使其变得简单。有趣的是,记忆也迅速增加。但是,如果我将dataType : "json"更改为dataType : "text",则内存使用量会非常小,GC显然会运行。在处理json时,似乎firefox有一些性能问题。

1 个答案:

答案 0 :(得分:3)

服务器端分页

如果"服务器"问题是运行一些编程语言/框架,如果您可以轻松地对该服务器上运行的代码进行更改,那么最简单的方法就是@Coder在评论中提到的内容。即:服务器端分页。

我将@Coder离开,为服务器分页提供答案"情况下。

客户端流式传输

另一方面,如果没有代码,您可以在服务器上进行更改(例如:它是由某些其他进程生成的静态JSON文件,并且有' s只有一个简单的静态HTTP服务器)然后你可能想采用我在评论中提到的方法,即:让客户端进行流式传输。

采用这种方法:

  1. 您向服务器请求整个JSON文件(无论是2MB还是更大) - 而不是使用XMLHttpRequest(AJAX),您在Firefox中使用本机平台API({{1} })。
  2. 当数据可从服务器读取时,您使用所谓的JSON" Pull Parser"在到达时逐步解析JSON文本
  3. 您解释了解析流程"事件"由pull解析器发出,以便将数据递增地写入本地存储(无论是数据库还是文件)。
  4. 注1:

    您无法在服务器端(显式或自动)使用GZIP编码,仍然可以在客户端上进行流式读取。您必须明确配置服务器以仅提供未压缩的JSON,或者您可以简单地使用StreamListener标头要求服务器不向您发送压缩内容(要求不存在压缩)方案如Accept-Encoding:)。

    注2:

    您无法通过内容'进行此类HTTP请求。 JavaScript(即:Web服务器提供的JavaScript内容)。您只能通过特权' JavaScript(例如:在Firefox插件中)。


    沙盒HTTP请求

    我不会包含一个自己提出请求的例子(它非常大),但你基本上只能剪切&从MDN上的Creating Sandboxed HTTP Connections页面粘贴第一个(最简单的)示例。

    在示例中,在dont-compress-please回调中,他们只是将读取的文本添加到缓冲区(字符串)中,如下所示:

    onDataAvailable

    相反,你想要让你的拉解析器'标记化'' 新读取的文本,并为当前读取的数据的任何完全解析部分发出事件(并且在内存中构建表示从服务器读取的文本的字符串)。

    JSON Pull Parser

    A'拉解析器'并不是一次解析一个字符串(比如说this.mData += scriptableInputStream.read(aLength); );而是 在您收到的源文本的 每个块 上调用其JSON.parse方法。解析器负责确定它在输入流中的位置(使用"状态机")以及何时开始或结束读取所谓的原子' (对于JSON,例如:tokenize()的开头,ArrayNumber的结尾,Array的开头等),它会调用您提供的回调(或发出一个事件来告诉你它。

    所以在你的情况下,假设JSON基本上是Object Array的{​​{1}}。你基本上应该忽略Object'的开头。然后为Array'的每个结尾您将单个Object中的数据写入您的语言环境存储。这样,你一次在内存中获得的最大的东西就是来自你的大文件的单行/元素。

    我发现了两个基于JavaScript的JSON Pull Parsers:

    您需要将您选择的解析器配置为" listen"在源文本中适当的开头/结尾。

    特权JavaScript

    由于Object界面具有特权,如果您仍然希望此网络应用程序主要在网络上运行(并且只有离线模式),则需要实施在Firefox插件中。

    此外,由于StreamListener回调的拉解析器 称为 ,因此它也需要在插件内实现为特权JavaScript。

    最简单的方法就是实现bootstrapped(a.k.a"无重启")Firefox插件,然后导出'您需要的功能'您的浏览器窗口,以便您的无特权内容' JavaScript代码可以使用它。

    我之前已经两次回答过这个问题(并且涉及相当多的代码):