我有一些代码,其中给了一个大的JSON字符串(可能是50MB到250MB),这是一个JSON对象数组,需要进行解析和清理,然后序列化为文件。使用50MB JSON字符串一切都很顺利但是当字符串超过一百MB时我的应用程序崩溃了OutOfMemoryError。我知道我可以增加堆的大小但是如果可能的话我希望避免这样做。我已经包含了我最近的一些想法。我尝试移动try块一点点无济于事。
1)我怀疑有一些方法可以使用流来执行此操作,但我不知道如何将结果String(json对象的json数组字符串)一次流式传输一个JSON对象。
2)由于结果是Java字符串,因此它是不可变的。我们如何消耗这个字符串并尽快将其从内存中删除?
3)每次清理结果是否更好地实例化一个新对象而不是每次只分配不同的对象?
4)在for循环结束时,不应该像循环之前那样使用大约2x内存,因为现在json stringbuilder变量包含与结果字符串相同的内存,结果字符串应该是内存中的两个最大变量?
我已经包含了以下代码。
String result = getLargeJSONString(...); // function that gives me a large JSON string which is an array of JSON objects
StringBuilder json = new StringBuilder(); // to hold final JSON values to write to file
// try to parse said large JSON String
JSONArray results = new JSONArray();
try {
results = new JSONArray(result);
} catch (JSONException j) {
j.printStackTrace();
}
// do json sanitation on each object and then append to stringbuilder
// note the final result should be a string with a JSON object on each newline
JSONObject cleanedResult = new JSONObject();
for (int i = 0; i < results.length(); i++) {
try {
cleanedResult = JSONSanitizer.sanitize((JSONObject) results.get(i));
} catch (JSONException j) {
cleanedResult = new JSONObject();
}
json.append(cleanedResult.toString());
json.append('\n');
}
// write built string to file
try {
Files.write(Paths.get("../file.json"), json.toString().getBytes());
} catch (IOException i) {
System.out.println(i);
}
答案 0 :(得分:1)
of corse 你应该优先通过连续的内存分配(String,StringBuilder,数组等)来处理大量数据。所以你最好的机会是使用流式JSON解析器/序列化器。
但是,您应该首先尝试通过几个简单的增益修复来优化您的代码:
一个:如果你真的需要在将结果写入文件之前存储结果,请将StringBuilder预先调整为它将具有的估计最大最终大小,因此它不需要在append
的每次执行时调整大小。例如,像这样:
StringBuilder json = new StringBuilder(result.length());
您甚至可以更好地考虑换行符的额外大小。例如,超大5%:
StringBuilder json = new StringBuilder((int)(1.05d*result.length()));
两个:如果您只需要将结果写入文件,请不要将其存储到StringBuilder中:
String result = getLargeJSONString(...);
JSONArray results = new JSONArray(result);
try(Writer output=new OutputStreamWriter(new FileOutputStream(outputFile), "UTF8")) {
for (int i = 0; i < results.length(); i++) {
JSONObject cleanedResult = JSONSanitizer.sanitize((JSONObject) results.get(i));
output.write(cleanedResult.toString());
output.write('\n');
}
}