我一直在处理一个问题语句,其中我们有一个巨大的JSON响应进入,当我们使用传统的gson解析技术解析它时,它通常会给予OutOfMemoryException,因为此方法在处理数据之前将其存储在内存中,因此作为对此问题的解决方案,我一直在处理JSON响应流,它不会将所有内容都放入内存中。所以它一直工作得很好,直到大约160万张唱片,在那之后甚至坏了。所以这是我们得到的例外。
线程“main”中出现异常java.lang.内存不足错误:Java堆空间
这是我用来做这件事的全部代码:
// Getting reponse into InputStream and casting it to JsonReader object for parsing
InputStream liInStream = luURLConn.getInputStream();
lCycleTimeReader = new JsonReader(new InputStreamReader(liInStream, "UTF-8"));
我们的JSON如下所示:
{
"Report_Entry": [
{
"key1": "value",
"key2": "value",
"key3": "value",
"key4": "value",
"key5": "value"
},
{
"key1": "value",
"key2": "value",
"key3": "value",
"key4": "value",
"key5": "value"
}
]}
使用这个对象进入我们的解析方法:
public HashMap<String, HashMap<String, String>> getcycleTimeMap(JsonReader poJSONReaderObj,
CycleTimeConstant cycleTimeConstant, int processId) {
Integer counter = 0;
HashMap<String, HashMap<String, String>> cycleTimeMap = new HashMap<String, HashMap<String, String>>();
HashMap<String, HashMap<String, String>> finalcycleTimeMap = new HashMap<String, HashMap<String, String>>();
try {
CycleTime cycleTime = new CycleTime();
poJSONReaderObj.beginObject();
while (poJSONReaderObj.hasNext()) {
String name = poJSONReaderObj.nextName();
if (name.equals("Report_Entry")) {
poJSONReaderObj.beginArray();
while (poJSONReaderObj.hasNext()) {
JsonToken nextToken2 = poJSONReaderObj.peek();
if (JsonToken.BEGIN_OBJECT.equals(nextToken2)) {
poJSONReaderObj.beginObject();
} else if (JsonToken.END_OBJECT.equals(nextToken2)) {
poJSONReaderObj.endObject();
} else {
String nextString = "";
if (JsonToken.STRING.equals(nextToken2)) {
nextString = poJSONReaderObj.nextString();
} else if (JsonToken.NAME.equals(nextToken2)) {
nextString = poJSONReaderObj.nextName();
}
switch (nextString) {
case "key1":
cycleTime.setKey1(poJSONReaderObj.nextString());
break;
case "key2":
cycleTime.setKey2(poJSONReaderObj.nextString());
break;
case "key3":
cycleTime.setKey3(poJSONReaderObj.nextString());
break;
case "key4":
cycleTime.setKey4(poJSONReaderObj.nextString());
break;
case "key5":
cycleTime.setKey5(poJSONReaderObj.nextString());
break;
}
}
poJSONReaderObj.endObject();
System.out
.println("Value of Map is : " + new Gson().toJson(cycleTime) + "counter : " + counter);
counter++;
System.out.println("Counter : " + counter);
cycleTimeMap = (HashMap<String, HashMap<String, String>>) cycleTimeBpProcessIterator(
cycleTime, cycleTimeConstant, counter, processId);
}
finalcycleTimeMap.putAll(cycleTimeMap);
}
}
JsonToken nextToken = poJSONReaderObj.peek();
if (JsonToken.END_OBJECT.equals(nextToken)) {
poJSONReaderObj.endObject();
} else if (JsonToken.END_ARRAY.equals(nextToken)) {
poJSONReaderObj.endArray();
}
} catch (IOException ioException) {
ioException.printStackTrace();
}
System.out.println("FINAL MAP TO BE LOADED : " + new Gson().toJson(finalcycleTimeMap));
return finalcycleTimeMap;
}
用于处理响应的POJO类:
public class CycleTime {
private String key1 = "";
private String key2 = "";
private String key3 = "";
private String key4 = "";
private String key5 = "";
public String getKey1() {
return key1;
}
public void setKey1(String key1) {
this.key1 = key1;
}
public String getKey2() {
return key2;
}
public void setKey2(String key2) {
this.key2 = key2;
}
public String getKey3() {
return key3;
}
public void setKey3(String key3) {
this.key3 = key3;
}
public String getKey4() {
return key4;
}
public void setKey4(String key4) {
this.key4 = key4;
}
public String getKey5() {
return key5;
}
public void setKey5(String key5) {
this.key5 = key5;
}
}
我不知道什么可能是这里的罪魁祸首,但似乎它是给同样的错误,我想知道什么应该是下一个方法,以避免这个内存溢出异常。
1条答案
按热度按时间jpfvwuh41#
将整个文档读入一个对象并不意味着流式读取会对您有所帮助。此外,Gson使用流式读取是因为它只是一种可选的读取和写入方式。然而,您的方法远远不够好:
hasNext
和beginObject
/endObject
对,您的反序列化器被破坏了)。Throwable.printStackTrace
(使用适当的日志记录设备);System.err
(这只是用于此目的的适当标准流);Integer
作为计数器是个坏主意,因为它会创建许多装箱值,尤其是对于大型文档(使用int
--它就很好);enum
值是否与==
相等(这是安全的,因为它们是单例);switch
来形成枚举(两者都比较短,编译时更安全);Gson
示例,特别是在有很多迭代的循环中(Gson
示例是不可变的,因为它是线程安全的,但在构造它的对象时并不便宜);HashMap
,而是Map
(如果有一天你需要另一个带有有序键的map怎么办?或者如果你根本不需要map怎么办?);CycleTime
和5个键怎么办?);这就是通过回调使用推送方法来减少内存占用的方法:
第一个
如您所见,在上面的运行程序中,您可以选择您喜欢的处理输入的方式:或者是一个哑日志记录,或者是一个简单计数器,或者是一个简单的collect-to操作。
有关通过
Stream
方法的拉动方法,请参见:https://stackoverflow.com/a/69282822/12232870