使用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 ...
随机推荐
- javascript学习-创建json对象数据,遍历
之前我已经有讲过后台返回json数据到前台,并在前台遍历json数据. 这里讲下直接在JS里创建JSON数据,然后遍历使用~ 创建代码例如以下:(创建的是JSON对象) var YearSelect ...
- wpf mvvm datagrid数据过滤
原文:wpf mvvm datagrid数据过滤 datagrid数据过滤,你可以通过设置RowStyle属性,通过将Visibility绑定到ViewModel层的属性来控制是否可见,比如: & ...
- jQuery.noop
一个空函数 当你仅仅想要传递一个空函数的时候,就用他吧.这对一些插件作者很有用,当插件提供了一个可选的回调函数接口,那么如果调用的时候没有传递这个回调函数,就用jQuery.noop来代替执行.
- Qt浅谈之十八:GraphicsView框架事件处理(有源码下载)
一.简介 GraphicsView支持事件传播体系结构,可以使图元在场景scene中得到提高了已被的精确交互能力.图形视图框架中的事件都是首先由视图进行接收,然后传递给场景,再由场景给相应的图形项. ...
- OpenGL(十二) 纹理映射(贴图)
OpenGL绘制纹理的步骤: 1. 开启纹理功能 使用glEnable(GL_TEXTURE_2D)开启2D纹理功能,使用glDisable(GL_TEXTURE_2D)关闭纹理,默认情况下纹理是关闭 ...
- 开源 自由 java CMS - FreeCMS1.9 积分规则管理
项目地址:http://www.freeteam.cn/ 积分规则管理 管理会员操作时积分处理规则. 1. 积分规则管理 从左側管理菜单点击积分规则进入. 2. 加入积分规则 在积分规则列表下方点击& ...
- DRP-ThreadLocal简单的理解
简单就是jar一类套餐包.在一个简单的事情是一个工具类!该工具可以做?该工具被用来写多线程程序,行.多线程是有效的.你只能去网上找资料,由于今天我们仅仅来介绍ThreadLocal的知识. 我们来 ...
- HDU-3839-Ancient Messages(DFS)
Problem Description In order to understand early civilizations, archaeologists often study texts wri ...
- socket 主机地址相关的函数
#include <arpa/inet.h> int inet_aton (const char *name, struct in_addr *addr) 将ipv4地址从数字点的形式转化 ...
- MVC 组件之间的关系
View和Controller都可以直接请求Model 但是Model不依赖View和controller lController可以直接请求View来显示具体页面 View不依赖Controller ...