2021SC@SDUSC
- JSONReader使用方法
- JSONReader源码分析
- startArray方法
- readString
- JSONReader性能测试
- Gson测试代码:
- Gson内存和cpu占用情况
- FastJSON测试代码
- FastJSON内存和cpu占用情况
- 总结
前面几篇文章分析的都是Fastjson对于少量json字符串的反序列化。我们不需要考虑内存的消耗,也难以察觉出Fastjson性能上的优越。但是当数据量庞大时,单纯的使用JSON.parseObject会产生严重问题,最明显的就是内存溢出。
通常解析json文件的思路是,先把整个json文件加载到内存中,然后一条一条进行反序列化。正常情况下我们都不需要考虑内存的问题,但是有时候我们需要处理一个拥有海量数据的文件,例如一个800MB大小的json文件,这时候我们就不能按照常规方法处理了。
我们需要的是一个能够一边读文件一边解析json的工具类。JSONReader可以帮助我们完成这一工作。 JSONReader使用方法
JSONReader reader = new JSONReader(new InputStreamReader(getAssets().open("goods.json"), "UTF-8")); reader.startArray();//开始解析json数组 while (reader.hasNext()) { reader.startObject();//开始解析json对象 Good good = new Good(); while (reader.hasNext()) { String key = reader.readString(); if ("id".equals(key)) { good.setId(reader.readString()); } else if ("name".equals(key)) { good.setName(reader.readString()); } else if ("price".equals(key)) { good.setPrice(Double.parseDouble(reader.readString())); } else if ("barCode".equals(key)) { good.setBarCode(reader.readString()); } else if ("desc".equals(key)) { good.setDesc(reader.readString()); } else if ("count".equals(key)) { good.setCount(Integer.parseInt(reader.readString())); } else { reader.readObject();//读取对象 } } reader.endObject();//结束解析对象 } reader.endArray();//结束解析数组 reader.close();关闭流 reader = null;
使用JSONReader对大文件进行解析,首先需要创建一个JSONReader对象,接受一个文件输入流作为参数,表明将要解析这个文件。接下来,调用startArray方法表示将要开始解析json数组,开始循环解析json字符串。在解析每一个json对象之前,调用startObject方法表明将要开始解析json字符串。每个字符串解析结束后调用endObject,最后,当所有json字符串解析完成,调用endArray结束解析,最后关闭流。
JSONReader源码分析 startArray方法public void startArray() { if (this.context == null) {//检查上下文是否为空 this.context = new JSONStreamContext((JSONStreamContext)null, 1004); } else { this.startStructure();//结构化json字符串 this.context = new JSONStreamContext(this.context, 1004); } this.parser.accept(14);//接收14号Token }
这个方法逻辑简单,先检查传入的流是否为空,若为空,直接以空的流来生成新的context对象。否则,先将json字符串结构化,再生成context对象。最后交给解析器对象14号Token,表明开始解析json数组。
为验证14号Token确实是json数组的标志,先进入package com.alibaba.fastjson.parser.JSONToken,找到对各个Token号的定义。14号的名称为LBRACKET,显然是某种左括号。
搜索一下英文中对各种括号的定义,可以看到bracket表示中括号[,确实是json数组开始的标志。
accept方法接收Token号之后与将解析的json字符串的下一个字符对比,如果下一字符与Token号相同,则返回;否则,抛出异常。这样保证了调用startArray之后,接下来要解析的内容一定是json数组。
endArray,startObject,endObject这三个方法的逻辑与其相似,不做重复分析。
readStringpublic String readString() { Object object; if (this.context == null) { object = this.parser.parse(); } else { this.readBefore(); JSONLexer lexer = this.parser.lexer; if (this.context.state == 1001 && lexer.token() == 18) { object = lexer.stringVal(); lexer.nextToken(); } else { object = this.parser.parse(); } this.readAfter(); } return TypeUtils.castToString(object); }
readString的作用是读取单个json字符串中下一个value值,并以String方式返回。其中的readBefore和readAfter用于处理一些json字符串中的注释、空格等可能影响字符串解析的因素,将主体部分保留下来用于生成对象并映射成String类型。
readInt,readLong,readObject等方法与该方法类似,区别只在于最后一步强制类型转换时使用了不同的类型,其中readObject方法可以接受类型名称,并以指定类型完成强制转换。
JSONReader性能测试分别用Gson和FastJson对同一个数据集进行解析,查看内存和cpu占用情况。
Gson测试代码:ListGson内存和cpu占用情况 FastJSON测试代码list = new Gson().fromJson(new InputStreamReader(getAssets().open("goods.json"), "UTF-8"), new TypeToken >() {}.getType());
JSONReader reader = new JSONReader(new InputStreamReader(getAssets().open("goods.json"), "UTF-8")); reader.startArray();//开始解析json数组 while (reader.hasNext()) { reader.startObject();//开始解析json对象 Good good = new Good(); while (reader.hasNext()) { String key = reader.readString(); if ("id".equals(key)) { good.setId(reader.readString()); } else if ("name".equals(key)) { good.setName(reader.readString()); } else if ("price".equals(key)) { good.setPrice(Double.parseDouble(reader.readString())); } else if ("barCode".equals(key)) { good.setBarCode(reader.readString()); } else if ("desc".equals(key)) { good.setDesc(reader.readString()); } else if ("count".equals(key)) { good.setCount(Integer.parseInt(reader.readString())); } else { reader.readObject();//读取对象 } } reader.endObject();//结束解析对象 } reader.endArray();//结束解析数组 reader.close();关闭流 reader = null;FastJSON内存和cpu占用情况
可以看到,两种方法下内存占用差异明显。测试使用的数据集约500M,实际生产条件下可能会有更大的json文件需要处理,如果不用FastJSON的JSONReader进行处理,必然会耗尽虚拟机内存。
总结FastJSON进行反序列化除了使用JSON类的静态方法外,还可以使用JSONReader类提供的方法,一边读文件一边解析json字符串,当json文件体积比较大时,此方法可以防止内存溢出。JSONReader提供的许多方法实现非常相似,区别在于返回时强制转换的类型不同,可以自由选择组合来达到自己的目的。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)