使用fastjson读取超巨json文件引起的GC问题
项目中需要将巨量数据生成的json文件解析,并写入数据库,使用了 alibaba 的 fastjson,在实践过程中遇到了 GC 问题,记录如下:
数据大约为70万条,文件大小在3~4G左右,使用 fastjson 官方推荐的 Stream Api 例3 的示例,在读取到30万数据时,内存使用量开始迅速上升,CPU也迅速达到百分之百,在读取到40万数据左右时,出现 GC。
代码如下:
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONReader;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.stereotype.Component;
import java.io.*;
import java.util.*; @Component
@Slf4j
public class EnterDatabaseUtils {
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate; private final int batchTotal = 50000; public boolean enterData(String databaseName, String tableName, File file, String[] fields) {
String fileName = file.getName();
try {
JSONReader reader = new JSONReader(new InputStreamReader(new FileInputStream(file.getAbsoluteFile()),"UTF-8"));
String insertSql = "INSERT INTO `" + databaseName + "`.`" + tableName + "`"
+ " (`" + StringUtils.join(fields, "`,`") + "`)"
+ " VALUES(:" + StringUtils.join(fields, ",:") + ")";
long count = 1;
ArrayList<Map<String, Object>> recordList = new ArrayList<>();
reader.startArray();
while (reader.hasNext()) {
reader.startObject();
JSONObject = reader.readObject(JSONObject.class);
if (count <= batchTotal) {
recordList.add(record);
count ++;
}
if (batchTotal + 1 == count) {
namedParameterJdbcTemplate.batchUpdate(insertSql, SqlParameterSourceUtils.createBatch(recordList));
count = 1;
recordList.clear();
}
}
if (recordList.size() > 0) {
namedParameterJdbcTemplate.batchUpdate(insertSql, SqlParameterSourceUtils.createBatch(recordList));
recordList.clear();
}
reader.endArray();
reader.close();
return true;
} catch (Exception e) {
log.error(databaseName + "." + tableName + ":插入失败");
log.error("", e);
return false;
}
}
}
测试代码:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.File; @RunWith(SpringRunner.class)
@SpringBootTest
public class EnterDatabaseUtilsTest { @Autowired
private EnterDatabaseUtils enterDatabaseUtils; @Test
public void testEnterDatabase() {
File file = new File("/xxx/xxx/xxx.json");
String[] fields = {........};
boolean res = enterDatabaseUtils.enterData("xxxx", "xxxx", file, );
} }
开始的时候,怀疑是 namedParameterJdbcTemplate 引起的内存占用疯涨。但是将所有的数据库相关操作删除,仅保留json读取代码,内存仍然疯涨并导致 GC。
遂怀疑是 fastjson 使用不当,阅读了大量文章之后,终于在 Json少量数据解析 一文中找到了答案:单行直接 readObject 会导致内存不断被消耗!
将代码改为使用 startObject 将每行中的 key、value 单独解析,内存和CPU占用稳定无增长,问题解决。
改进后的代码如下:
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONReader;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.stereotype.Component;
import java.io.*;
import java.util.*; @Component
@Slf4j
public class EnterDatabaseUtils {
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate; private final int batchTotal = 50000; public boolean enterData(String databaseName, String tableName, File file, String[] fields) {
String fileName = file.getName();
try {
JSONReader reader = new JSONReader(new InputStreamReader(new FileInputStream(file.getAbsoluteFile()),"UTF-8"));
String insertSql = "INSERT INTO `" + databaseName + "`.`" + tableName + "`"
+ " (`" + StringUtils.join(fields, "`,`") + "`)"
+ " VALUES(:" + StringUtils.join(fields, ",:") + ")";
long count = 1;
ArrayList<Map<String, Object>> recordList = new ArrayList<>();
Map<String, Object> record = new HashMap<>();
reader.startArray();
while (reader.hasNext()) {
reader.startObject();
while (reader.hasNext()) {
record.put(reader.readString(), reader.readObject());
}
reader.endObject();
if (count <= batchTotal) {
recordList.add(record);
count ++;
}
if (batchTotal + 1 == count) {
namedParameterJdbcTemplate.batchUpdate(insertSql, SqlParameterSourceUtils.createBatch(recordList));
count = 1;
recordList.clear();
}
}
if (recordList.size() > 0) {
namedParameterJdbcTemplate.batchUpdate(insertSql, SqlParameterSourceUtils.createBatch(recordList));
recordList.clear();
}
reader.endArray();
reader.close();
return true;
} catch (Exception e) {
log.error(databaseName + "." + tableName + ":插入失败");
log.error("", e);
return false;
}
}
}
使用fastjson读取超巨json文件引起的GC问题的更多相关文章
- 读取本地的json文件
最近写项目需要读取本地的json文件,然后悲催的发现前端新手的我居然不会,查查找找发现这东西并不难,但是应该是比较常用的,毕竟json太好用了! 我是直接用的jquery实现的,但是Ajax也可以,不 ...
- Json.NET读取和写入Json文件
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...
- java 读取本地的json文件
首先,要先去下载相关的jar包,否则你是无法做到的. 在百度或者谷歌里面输入java json jar包下载就行了(共7个包). xom-1.1.jar ezmorph-1.0.6.jar json ...
- java读取url中json文件中的json数据
有时候需要远程从其他接口中获取json数据,如果遇到返回的json数据是一个文件而不直接是数据,那么可以通过以下方法进行读取: /** * 从数据接口获取到数据 * @return * @throws ...
- 以字符串形式读取github上.json文件
如下: https://github.com/hpu-spring87/ebooks/blob/master/update.json 如果直接用httpclient读取该URL地址,得到结果是这样的: ...
- 读取静态的json文件
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- Java 读取Json文件内容
读取json文件为String类型: import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logge ...
- jquery无法读取json文件问题
jquery无法读取json文件,如:user.json文件无法读取.把user.json文件的后缀名修改为aspx,文件内容不变,则可以读取~ 原理不懂!~~
- 解决:java 读取 resources 下面的 json 文件
前言:java 读取 工程下的配置文件,文件类型为 json(*.json),记录一下始终读取不到 json 文件的坑.maven项目 直接上工具类代码 package com.yule.compon ...
随机推荐
- octopress第三方插件:博文同步工具syncPost
为了增加外链等考虑,独立博客往往有将博文同步到其他博客社区的需求.自己人肉黏贴的方式笨拙.重复,对于程序猿而言,着实不可取. 我在 github 上找到了 syncPost 这个针对 octopres ...
- Project Euler:Problem 39 Integer right triangles
If p is the perimeter of a right angle triangle with integral length sides, {a,b,c}, there are exact ...
- muduo源代码分析--Reactor在模型muduo使用(两)
一. TcpServer分类: 管理所有的TCP客户连接,TcpServer对于用户直接使用,直接控制由用户生活. 用户只需要设置相应的回调函数(消息处理messageCallback)然后TcpSe ...
- python 合并两个排序的链表
题目描述 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则. 样例 给出 1->3->8->11->15->null,2-& ...
- Qt、Qte与Qtopia(Qt嵌入式的发展历程)
Qt的授权是分为两条线,商业版和开源版.如果使用商业版的Qt,那么开发出的程序可以是私有的和商业的:如果使用的是开源版的Qt,由于其使用的是GPL协议,那么可发出的程序也必须是GPL的.不过自从qt ...
- 自定义Behavior 实现Listbox自动滚动到选中项
原文:自定义Behavior 实现Listbox自动滚动到选中项 blend为我们提供方便的behavior来扩展我们的控件,写好之后就可以在blend中方便的使用了. 下面是自定义的behavior ...
- Codeforces Beta Round #7--D. Palindrome Degree(Manacer)
题目:http://blog.csdn.net/winddreams/article/details/44218961 求出每一个点为中心的最长字符串,推断该串是不是从开头的回文串. #include ...
- Java Swing编程接口(30)---列表框:JList
列表框同时可以在信息呈现给用户的列表多个选项,使用JList能够建立一个列表框. package com.beyole.util; import java.awt.Container; import ...
- Entity framework 更改模型,新增表
在Package Manager Console 中运行命令Enable-Migrations 再次运行可以更新 抄袭 在实体类中增加一个属性以后,执行 Update-Database 命令 ,可以更 ...
- jqmobi api 详细解说
0.$().get()得到是相应的元素. 如: $elem=$(".panal").get(0)); //得到了第一个panal类的元素 $elem_id = $elem.id ...