使用fastjson时堆溢出

1. 问题描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private JSONArray getChildInfo(String root, Set<String> childrens) throws JSONException {
JSONArray rtJA = new JSONArray();
for (String child : childrens) {
String key = generateKey(root,child);
if (isVisited.contains(key)) {
continue;
}

String simpleTableName = child.substring(child.indexOf('@')+1);
effectNode.add(simpleTableName);
isVisited.add(key);

JSONObject jsonObject = new JSONObject();
if(nodechildrens.containsKey(child)){
JSONArray temp = getChildInfo(child, nodechildrens.get(child));
if (!temp.isEmpty()) {
jsonObject.put("children", temp.toJSONString());
} else {
jsonObject.put("children", "");
}
} else {
jsonObject.put("children", "");
}
jsonObject.put("name",simpleTableName);
isVisited.remove(key);
rtJA.add(jsonObject.toString());
}
return rtJA;
}

temp.toJSONString()报错

1
fastjson java.lang.OutOfMemoryError: Java heap space

因为代码是里面用了递归,一开始以为是代码问题,debug了许久,没发现哪里逻辑错了。
想着之前学过JVM,就试着通过堆快照分析哪里错了

2. 堆快照分析

首先,设置jvm参数(jdk 1.8),使发生堆溢出时保存快照

1
-XX:HeapDumpPath=/Users/wangji/Desktop/dumpfile.hprof -XX:+HeapDumpOnOutOfMemoryError

之后,通过MAT分析快照

发现有个String有455MB的大小,使用fastjson时,又占了445MB,整体接近1G,这里才是发生堆溢出的原因。
知道原因后,递归过程中对字符串进行简化操作(去除”\“和其他多余的字符),最终解决问题

简化代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private JSONArray getChildInfo(String root, Set<String> childrens) throws JSONException {
JSONArray rtJA = new JSONArray();
for (String child : childrens) {
String key = generateKey(root,child);
if (isVisited.contains(key)) {
continue;
}

String simpleTableName = child.substring(child.indexOf('@')+1);
effectNode.add(simpleTableName);
isVisited.add(key);

JSONObject jsonObject = new JSONObject();
if(nodechildrens.containsKey(child)){
JSONArray temp = getChildInfo(child, nodechildrens.get(child));
if (!temp.isEmpty()) {
// 主要是这句的字符串引起了堆内存溢出,对字符串简化
jsonObject.put("children", modifyString(temp.toJSONString(),false));
} else {
jsonObject.put("children", "");
}
} else {
jsonObject.put("children", "");
}
jsonObject.put("name",simpleTableName);
isVisited.remove(key);
rtJA.add(jsonObject.toString());
}
return rtJA;
}

private String modifyString(String origin, boolean removeSide) {
String out = origin.replaceAll("\\\\","");
out = out.replaceAll("\"\\{","{");
out = out.replaceAll("\\}\"","}");
out = out.replaceAll("\"\\[","[");
out = out.replaceAll("\\]\"","]");
if (removeSide) {
out = out.substring(1,out.length()-1);
}
return out;
}