项目中需要将巨量数据生成的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问题的更多相关文章

  1. 读取本地的json文件

    最近写项目需要读取本地的json文件,然后悲催的发现前端新手的我居然不会,查查找找发现这东西并不难,但是应该是比较常用的,毕竟json太好用了! 我是直接用的jquery实现的,但是Ajax也可以,不 ...

  2. Json.NET读取和写入Json文件

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  3. java 读取本地的json文件

    首先,要先去下载相关的jar包,否则你是无法做到的. 在百度或者谷歌里面输入java json  jar包下载就行了(共7个包). xom-1.1.jar ezmorph-1.0.6.jar json ...

  4. java读取url中json文件中的json数据

    有时候需要远程从其他接口中获取json数据,如果遇到返回的json数据是一个文件而不直接是数据,那么可以通过以下方法进行读取: /** * 从数据接口获取到数据 * @return * @throws ...

  5. 以字符串形式读取github上.json文件

    如下: https://github.com/hpu-spring87/ebooks/blob/master/update.json 如果直接用httpclient读取该URL地址,得到结果是这样的: ...

  6. 读取静态的json文件

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...

  7. Java 读取Json文件内容

    读取json文件为String类型: import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logge ...

  8. jquery无法读取json文件问题

    jquery无法读取json文件,如:user.json文件无法读取.把user.json文件的后缀名修改为aspx,文件内容不变,则可以读取~ 原理不懂!~~

  9. 解决:java 读取 resources 下面的 json 文件

    前言:java 读取 工程下的配置文件,文件类型为 json(*.json),记录一下始终读取不到 json 文件的坑.maven项目 直接上工具类代码 package com.yule.compon ...

随机推荐

  1. windows 下 TensorFlow(GPU 版)的安装

    windows 10 64bit下安装Tensorflow+Keras+VS2015+CUDA8.0 GPU加速 0. 环境 OS:Windows 10,64 bit: 显卡:NVIDIA GeFor ...

  2. 常见Python 中pip用法(待继续添加)

    常见pip用法 * pip install numpy             --安装包numpy * pip uninstall numpy           --卸载包numpy * pip ...

  3. auxiliary variable(辅助变量)的引入

    辅助变量的引入是推导数学公式的一个重要手段. 1. 条件概率 ⇒ 积分 P(x=1|D)=∫10P(x=1|μ)P(μ|D)dμ=∫10μP(μ|D)dμ=E(μ|D) 2. 条件概率 ⇔ 边缘概率 ...

  4. 科学的解决Http Token拦截器TokenInterceptor实现

    1.写在前面 在做项目的时候,有时对接口要求比较严谨.先介绍下情况. 我这边Http 方式采用的是 OKhttp+Retrofit 后台一共分为三种token,分别是实名token(accessTok ...

  5. 使用google自带包实现下拉刷新功能

    android 实现下拉刷新有非常多开源的源代码能够用 比方 :PullToRefreshListView  使用起来也非常方便 如今还能够直接使用google libs以下的 android-sup ...

  6. quick-cocos2d-x游戏开发【8】——动画与动作

    动画与动作,在quick中都有对其封装,所以我们还是来看一下吧. 总的来说,对于帧动画,quick封装的方法我们能够常常使用,这是很方便的,以下直接上代码来直观感受下, 比方,14张帧图片,採用coc ...

  7. swagger ui 值类型形参加文字注释

    例: @ApiImplicitParams({ @ApiImplicitParam(paramType = "query", name = "indexCondition ...

  8. springboot 集成swagger ui

    springboot 配置swagger ui 1. 添加依赖 <!-- swagger ui --> <dependency> <groupId>io.sprin ...

  9. iOS_9_scrollView分页

    最后效果图: BeyondViewController.h // // BeyondViewController.h // 8_scrollVIew分页浏览 // // Created by beyo ...

  10. WPF listview item mouse enter/over popup

    This is because the routing strategy of the Loaded event is Direct, which means that the routed even ...