避免 OutOfMemoryError
Avoiding OutOfMemoryError
我有一个代码,其中我得到了一个大的 JSON 字符串(可以是 50MB 到 250MB 之间的任何地方),它是一个 JSON 对象的数组,要被解析和清理然后序列化为文件。使用 50MB JSON 字符串一切正常,但是当字符串超过 100 MB 左右时,我的应用程序因 OutOfMemoryError 而崩溃。我知道我可以增加堆的大小,但如果可能的话我想避免这样做。我已经包含了我最近的一些想法。我尝试稍微移动 try 块但无济于事。
1) 我怀疑有一些方法可以用流来做到这一点,但我不知道如何流式传输结果字符串(它是 json 对象的 json 数组字符串)一个 JSON 个对象。
2) 由于结果是 Java 字符串,因此它是不可变的。我们如何使用这个字符串并尽快将其从内存中取出?
3) cleanedResult 每次都实例化一个新对象会更好,而不是每次都为同一个对象分配不同的东西吗?
4) 在 for 循环的末尾,不应该只使用大约 2 倍于循环之前的内存,因为现在 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);
}
在 corse 中,您应该更喜欢流式传输而不是连续内存分配(String、StringBuilder、数组等)来处理大量数据。所以你最好的机会是使用流媒体 JSON parser/serializer.
但是,您应该首先尝试通过几个容易获得的修复来优化您的代码:
One:如果您确实需要在将结果写入文件之前存储结果,请将 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');
}
}
我有一个代码,其中我得到了一个大的 JSON 字符串(可以是 50MB 到 250MB 之间的任何地方),它是一个 JSON 对象的数组,要被解析和清理然后序列化为文件。使用 50MB JSON 字符串一切正常,但是当字符串超过 100 MB 左右时,我的应用程序因 OutOfMemoryError 而崩溃。我知道我可以增加堆的大小,但如果可能的话我想避免这样做。我已经包含了我最近的一些想法。我尝试稍微移动 try 块但无济于事。
1) 我怀疑有一些方法可以用流来做到这一点,但我不知道如何流式传输结果字符串(它是 json 对象的 json 数组字符串)一个 JSON 个对象。
2) 由于结果是 Java 字符串,因此它是不可变的。我们如何使用这个字符串并尽快将其从内存中取出?
3) cleanedResult 每次都实例化一个新对象会更好,而不是每次都为同一个对象分配不同的东西吗?
4) 在 for 循环的末尾,不应该只使用大约 2 倍于循环之前的内存,因为现在 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);
}
在 corse 中,您应该更喜欢流式传输而不是连续内存分配(String、StringBuilder、数组等)来处理大量数据。所以你最好的机会是使用流媒体 JSON parser/serializer.
但是,您应该首先尝试通过几个容易获得的修复来优化您的代码:
One:如果您确实需要在将结果写入文件之前存储结果,请将 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');
}
}