使用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 ...
随机推荐
- PAT 1065 - 1068 题解
这次的题目来源是 2013 年 10 月 7 日下午的浙大计算机研究生招生机试题. 这次题目的难度,按姥姥的说法是:『比普通的 PAT 要难了 0.5 个点.我是把自己的题目从 1.0 到 5.0 以 ...
- 数学概念 —— 奇异性(Singularity,Vertical tangent)
0. 基本定义 Singularity (mathematics) 数学上的奇异性一般是指,函数在该点未定义(not defined,比如取值为无穷),或者不可微(fails to be well-b ...
- eclipes 常用的快捷键 , 修改字体
内容辅助键 Alt+/ 自动补齐main方法 main 然后 Alt+/ 自动补齐输出语句 syso 然后 Alt+/ 格式化Ctrl+Shift+f 代码区域右键 -- Source – Fo ...
- zabbix 设备(自己的实践)
1. 下载源代码包 wget http://sourceforge.net/projects/zabbix/files/ 2. 解压 tar -zxvf zabbix-2.2.3.tar.gz 3. ...
- java学习笔记(2)——函数
int a = 1; a = a++; 1,取出a的值1作为a++表达式的值(a++表达式等于1) 2,a增加1变为2 3,执行赋值运算,a++表达式的值再赋给a,a又成为了1. ---------- ...
- 奥格尔巧妙kfifo
奥格尔巧妙kfifo Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn.net/chen19870707 Date:O ...
- 经典数字图像处理(matlab 实现)
Ivan Selesnick(Software) Sparsity / Total variation / Denoising Software - Michael Elad's Personal P ...
- 大数据_zookeeper环境搭建中的几个坑
文章目录 [] Zookeeper简介 关于zk的介绍, zk的paxos算法, 网上已经有各位大神在写了, 本文主要写我在搭建过程中的几个极有可能遇到的坑. Zookeeper部署中的坑 坑之一 E ...
- js动态创建表格
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- 最好的方式是用VirtualAlloc分配虚拟内存,它既不是在堆也不是在栈,而是直接在进程的地址空间中保留一块内存
申请效率的比较 栈:由系统自动分配,速度较快.但程序员是无法控制的. 堆:是由new分配的内存,最好的方式是用VirtualAlloc分配虚拟内存,它既不是在堆也不是在栈,而是直接在进程的地址空间中保 ...