项目中需要将巨量数据生成的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. Java--基础命名空间和相关东西(JAVA工程师必须会,不然杀了祭天)

    java.lang (提供利用 Java 编程语言进行程序设计的基础类)java.lang.annotation(提供了引用对象类,支持在某种程度上与垃圾回收器之间的交互)java.lang.inst ...

  2. Android显示gif格式图片

    大家知道,在Android中使用ImageView来显示gif格式的图片,我们无法得到gif格式图片该有的效果,它只会停在第一帧上,而不会继续.这时只能看到一张静态的图片,这里我们可以使用个简单的方法 ...

  3. iOS开展block说明

    源代码下载 浅谈block使用方法 对于block他用着确实方便,好多人都非常迷茫,这里写了一个Demo解说block的使用方法 好多人都觉得block是用于后一个界面向前一个界面传值用的,事实上更详 ...

  4. wxWindows

    用C++编写跨平台程序 中文版说明 本教程由Gxl117翻译并将继续维护,这是本教程的第一稿,假设发现错误请与我(Email:gxl117@yahoo.com.cn)联系让我能及时修正它.之后还会对这 ...

  5. Java 中override、overload、overwrite区别,以及与多态的关系【转】

    因为早期的翻译导致了override和overwrite的解释及理解混乱,需要重新梳理这几个词及相关内容. 转自:http://blog.csdn.net/lzhang007/article/deta ...

  6. python3下的IE自动化模块PAMIE

    PAMIE是Python下面的用于IE的自动化模块,支持python2和python3,python3的下载地址:http://sourceforge.net/projects/pamie/files ...

  7. Excel 2013永久取消超链接

    原文:Excel 2013永久取消超链接 在使用Excel的过程中,Excel会自动将网址转换为超链接,操作不当,容易误点,引起不必要的错误, 那么本篇博客就总结下如何在Excel 2013里永久取消 ...

  8. 写在程序猿的困惑(特别Java程序猿)入行一年,感觉我不知道接下来该怎么办才能不断进步的,寻求翼

    入行了一年.感觉不知道接下来该怎么做才干继续进步了,求不吝赐教(V2EX) @kafka0102 :做技术能够学的东西太多了.仅仅是在不同的阶段做好不同的规划.要结合当前所做的事情去做更深入或广度的学 ...

  9. DNS查询工具:host、nslookup、dig

    作者:zhanhailiang 日期:2014-11-01 1. host host提供域名到IP地址的双向解析: host默认通过/etc/resolv.conf读取Name Server来解析,除 ...

  10. 解决ASP.NET中Redis 每小时6000次访问请求的问题

    原文:解决ASP.NET中Redis 每小时6000次访问请求的问题 虽然ServiceStack v4是商业支持的产品,但我们也允许免费使用小型项目和评估目的.上面的NuGet包中包含可以使用许可证 ...