Ahm*_*sih 11 javascript arrays parsing json node.js
我生成一个~200'000元素的对象数组(使用对象文字符号map而不是new Constructor()),我将它的JSON.stringify'd版本保存到磁盘,它需要31 MB,包括换行符和一个空格 - per-indentation level(JSON.stringify(arr, null, 1)).
然后,在新的节点进程中,我将整个文件读入UTF-8字符串并将其传递给JSON.parse:
var fs = require('fs');
var arr1 = JSON.parse(fs.readFileSync('JMdict-all.json', {encoding : 'utf8'}));
Run Code Online (Sandbox Code Playgroud)
根据Mavericks的Activity Monitor,节点内存使用量约为1.05 GB!即使打入终端,我的古老4 GB RAM机器也会感觉更加懒散.
但是,如果在一个新的节点进程中,我将文件的内容加载到一个字符串中,在元素边界处切断它,并且JSON.parse每个元素单独地,表面上获得相同的对象数组:
var fs = require('fs');
var arr2 = fs.readFileSync('JMdict-all.json', {encoding : 'utf8'}).trim().slice(1,-3).split('\n },').map(function(s) {return JSON.parse(s+'}');});
Run Code Online (Sandbox Code Playgroud)
节点只使用~200 MB的内存,没有明显的系统滞后.这种模式在节点的多次重启中持续存在:JSON.parse整个数组需要一大堆内存,而在元素方面解析它需要更高的内存效率.
为什么内存使用存在如此巨大的差异?这是否会JSON.parse妨碍在V8中生成有效的隐藏类?如何在没有切片和切块的情况下获得良好的内存性能?我必须使用流式JSON解析吗?
为了便于实验,我将JSON文件置于Gist中,请随意克隆它.
需要注意的几点:
JSON.parse()对阵列的每个元素进行单独调用而不是一个大的调用会更有效JSON.parse().这表明了一个简单的解决方案:不是生成一个巨大的JSON数组,而是为数组的每个元素生成一个单独的JSON字符串 - JSON字符串中没有换行符,即只使用JSON.stringify(item)不带space参数.然后将这些JSON字符串与换行符(或您知道将永远不会出现在数据中的任何字符)一起加入并写入该数据文件.
读取此数据时,将换行数据拆分为换行符,然后分别JSON.parse()对每行进行分析.换句话说,这一步就像你的第二个解决方案,但是用一个简单的字符串拆分而不是必须摆弄字符数和花括号.
您的代码可能看起来像这样(实际上只是您发布的简化版本):
var fs = require('fs');
var arr2 = fs.readFileSync(
'JMdict-all.json',
{ encoding: 'utf8' }
).trim().split('\n').map( function( line ) {
return JSON.parse( line );
});
Run Code Online (Sandbox Code Playgroud)
正如您在编辑中所述,您可以将此代码简化为:
var fs = require('fs');
var arr2 = fs.readFileSync(
'JMdict-all.json',
{ encoding: 'utf8' }
).trim().split('\n').map( JSON.parse );
Run Code Online (Sandbox Code Playgroud)
但我会小心这一点.它在这种特殊情况下确实有效,但在更一般的情况下存在潜在的危险.
该JSON.parse函数有两个参数:JSON文本和可选的"reviver"函数.
该[].map()函数将三个参数传递给它调用的函数:项值,数组索引和整个数组.
所以,如果你通过JSON.parse直接,它被称为用JSON文本作为第一个参数(如预期),但它也正在通过一个号码为"齐磊"功能.JSON.parse()忽略第二个参数因为它不是函数引用,所以你在这里就可以了.但是你可以想象一下你可能遇到麻烦的其他情况 - 所以当你传递一个你没有写入的任意函数时,重复检查这个问题总是一个好主意[].map().