Android 巨大的 JSONObject toString 出现 OutOfMemoryError
Android huge JSONObject toString goes OutOfMemoryError
我需要从 SQLite 数据库中读取大量数据并创建格式正确的 JSON。
我目前正在通过一个名为 RequestPayload
的对象来实现这一目标,该对象包含一些 ArrayList
,我将我从 SQLite 读取的数据放入其中。
RequestPayload
class 有一个 parseJson()
方法, returns 一个 JSONObject
我最终调用 toString()
方法来获取我的 JSON 最终写入文件的字符串。
问题
现在,当我在 SQLite 中获得“少量”数据时,一切正常。当我获得大量数据时,会发生这种情况:
06-28 09:55:34.121 10857-6799/it.example.sampler E/AndroidRuntime: FATAL EXCEPTION: Thread-195240
Process: it.example.sampler, PID: 10857
Theme: themes:{com.cyanogenmod.trebuchet=overlay:system, com.android.settings=overlay:system, default=overlay:system, iconPack:system, fontPkg:system, com.android.systemui=overlay:system, com.android.systemui.navbar=overlay:system}
java.lang.OutOfMemoryError: Failed to allocate a 52962812 byte allocation with 16764752 free bytes and 41MB until OOM
at java.lang.StringFactory.newStringFromChars(Native Method)
at java.lang.AbstractStringBuilder.toString(AbstractStringBuilder.java:629)
at java.lang.StringBuilder.toString(StringBuilder.java:663)
at org.json.JSONStringer.toString(JSONStringer.java:430)
at org.json.JSONObject.toString(JSONObject.java:690)
at it.example.sampler.controllers.network.RequestBodyEncoder.serialise(RequestBodyEncoder.java:69)
at it.example.sampler.controllers.network.RequestBodyEncoder.createPacket(RequestBodyEncoder.java:50)
at it.example.sampler.services.FileStoreRunnable.run(FileStoreRunnable.java:57)
at java.lang.Thread.run(Thread.java:818)
06-28 09:55:34.509 10857-10857/it.example.sampler E/WindowManager: android.view.WindowLeaked: Activity it.example.sampler.ui.StartSamplingActivity has leaked window com.android.internal.policy.PhoneWindow$DecorView{74710d V.E...... R......D 0,0-1026,494} that was originally added here
at android.view.ViewRootImpl.<init>(ViewRootImpl.java:372)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:299)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:86)
at android.app.Dialog.show(Dialog.java:319)
at it.example.sampler.ui.StartSamplingActivity.executeStop(StartSamplingActivity.java:305)
at it.example.sampler.ui.StartSamplingActivity.onClickedButton(StartSamplingActivity.java:256)
at it.example.sampler.ui.StartSamplingActivity.onClick(StartSamplingActivity.java:190)
at android.view.View.performClick(View.java:5204)
at android.view.View$PerformClick.run(View.java:21158)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5461)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
代码
错误行来自此方法:
private String serialise() throws JSONException {
// Serialise
String sampleString = mPayload.parseJSON().toString(); // THIS LINE
Logger.get().d(LOG_TAG, "Serialised payload: -> " + sampleString);
return sampleString;
}
如果我得到 LogCat 正确,错误是在执行 toString()
方法期间,看起来 JSON 对象太大。
我该如何处理这种情况?
更新:
回答@YashJain 评论询问 JSONObject.
的大小
采样持续 2 小时后,我有一个 JSON 包含:
- 12 JSON数组包含 155.432
float
s
- 2 JSON包含8.393的数组
float
s
- 1 JSON包含 8.393 个自定义对象的数组,每个对象包含 5
float
s(和一个字符串)
- 1 包含 155.432
int
s 的二维数组
就字节而言,因为 Float 是由 4 个字节组成的(我希望我已经正确地完成了微积分)我得到了大约:
(12 * 155432 * 4) + (2 * 8393 * 4) + (1 * 8393 * 5) + (1 * 155432 * 4) ~= 8.191.573 bytes
更新:
有人问我使用的压缩算法。我通过 Deflater
和 DeflaterOutputStream
Java classes.
使用 ZLIB
因此流程是:
- 示例数据 -> 将它们存储在
SQLite
- 从
SQLite
读取 -> 在内存中构建 RequestPayload
对象
- 将
RequestPayload
对象转换为 JSONObject
(使用自定义解析器)。
- 将
JSONObject
转换为字符串(使用 JSONObject
toString()
方法)
- 压缩字符串字节(使用
getBytes()
)-> 将其编码为 Base64
- 将最终的 Base64 字符串发送到服务器
编辑:
为了解决“可能的重复标志”问题,我在研究期间没有看到这个问题,因为它使用了 largeHeap
指令(我不使用),所以对我没有帮助。也没有公认的答案,唯一的答案实际上并没有提供实用的解决方案。
你的Qn很好。
您是否尝试过输入 AsyncTask
也许它会通过 Thread
处理来工作。
private String serialise() throws JSONException {
String sampleString = "";
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground( Void... voids ) {
// Serialise
sampleString = mPayload.parseJSON().toString(); // THIS LINE.
return null;
}
}.execute();
Logger.get().d(LOG_TAG, "Serialised payload: -> " + sampleString);
return sampleString;
}
我假设你有很好的知识。
抱歉,如果它可能不起作用。但它确实在我的情况下。请查收。
你会遇到巨大的Jsons转换问题,当转换为String时,系统会将全部内存分配给一个唯一变量,因此你会发生内存溢出。
最好的处理方法是进行一些 Json 流式传输,这将读取 Json 的部分内容并对其进行解析。
我认为重新发明轮子没有意义,所以我建议使用 Json 解析器库,例如 Gson 或 Jackson,它们会自动将您的 json 转换为 POJO,您可以存储它们在 SQLite 数据库中,您可以使用一些数据库辅助工具,例如 ORMLite。
这样就可以接收到一个巨大的JSON,直接将他解析成一个POJO对象,然后将POJO直接插入到数据库中,内存管理交给库
在这个site中你可以解析你JSON并以GSON或Jackson格式检索整个class,之后,你只需要添加ORMLite注释并创建数据库助手,真的很简单
很抱歉没有再提供任何反馈我一直忙于其他工作,这在我的优先队列中排在最后。
无论如何,真正对我有用的是使用 Google GSON
库。
我的连载方式变得简单了:
private String serialise() {
// Serialise
return mPayload.toStringFromGSON();
}
class Payload
中的方法 toStringFromGSON()
是:
public String toStringFromGSON() {
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
return gson.toJson(this);
}
总之我觉得这也算是临时补丁吧。事实上,我注意到应用程序使用的内存在这个阶段急剧增加,因此我认为这个问题只是被推迟了,最终会在更重的负载下发生..
我需要从 SQLite 数据库中读取大量数据并创建格式正确的 JSON。
我目前正在通过一个名为 RequestPayload
的对象来实现这一目标,该对象包含一些 ArrayList
,我将我从 SQLite 读取的数据放入其中。
RequestPayload
class 有一个 parseJson()
方法, returns 一个 JSONObject
我最终调用 toString()
方法来获取我的 JSON 最终写入文件的字符串。
问题
现在,当我在 SQLite 中获得“少量”数据时,一切正常。当我获得大量数据时,会发生这种情况:
06-28 09:55:34.121 10857-6799/it.example.sampler E/AndroidRuntime: FATAL EXCEPTION: Thread-195240
Process: it.example.sampler, PID: 10857
Theme: themes:{com.cyanogenmod.trebuchet=overlay:system, com.android.settings=overlay:system, default=overlay:system, iconPack:system, fontPkg:system, com.android.systemui=overlay:system, com.android.systemui.navbar=overlay:system}
java.lang.OutOfMemoryError: Failed to allocate a 52962812 byte allocation with 16764752 free bytes and 41MB until OOM
at java.lang.StringFactory.newStringFromChars(Native Method)
at java.lang.AbstractStringBuilder.toString(AbstractStringBuilder.java:629)
at java.lang.StringBuilder.toString(StringBuilder.java:663)
at org.json.JSONStringer.toString(JSONStringer.java:430)
at org.json.JSONObject.toString(JSONObject.java:690)
at it.example.sampler.controllers.network.RequestBodyEncoder.serialise(RequestBodyEncoder.java:69)
at it.example.sampler.controllers.network.RequestBodyEncoder.createPacket(RequestBodyEncoder.java:50)
at it.example.sampler.services.FileStoreRunnable.run(FileStoreRunnable.java:57)
at java.lang.Thread.run(Thread.java:818)
06-28 09:55:34.509 10857-10857/it.example.sampler E/WindowManager: android.view.WindowLeaked: Activity it.example.sampler.ui.StartSamplingActivity has leaked window com.android.internal.policy.PhoneWindow$DecorView{74710d V.E...... R......D 0,0-1026,494} that was originally added here
at android.view.ViewRootImpl.<init>(ViewRootImpl.java:372)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:299)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:86)
at android.app.Dialog.show(Dialog.java:319)
at it.example.sampler.ui.StartSamplingActivity.executeStop(StartSamplingActivity.java:305)
at it.example.sampler.ui.StartSamplingActivity.onClickedButton(StartSamplingActivity.java:256)
at it.example.sampler.ui.StartSamplingActivity.onClick(StartSamplingActivity.java:190)
at android.view.View.performClick(View.java:5204)
at android.view.View$PerformClick.run(View.java:21158)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5461)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
代码
错误行来自此方法:
private String serialise() throws JSONException {
// Serialise
String sampleString = mPayload.parseJSON().toString(); // THIS LINE
Logger.get().d(LOG_TAG, "Serialised payload: -> " + sampleString);
return sampleString;
}
如果我得到 LogCat 正确,错误是在执行 toString()
方法期间,看起来 JSON 对象太大。
我该如何处理这种情况?
更新: 回答@YashJain 评论询问 JSONObject.
的大小采样持续 2 小时后,我有一个 JSON 包含:
- 12 JSON数组包含 155.432
float
s - 2 JSON包含8.393的数组
float
s - 1 JSON包含 8.393 个自定义对象的数组,每个对象包含 5
float
s(和一个字符串) - 1 包含 155.432
int
s 的二维数组
就字节而言,因为 Float 是由 4 个字节组成的(我希望我已经正确地完成了微积分)我得到了大约:
(12 * 155432 * 4) + (2 * 8393 * 4) + (1 * 8393 * 5) + (1 * 155432 * 4) ~= 8.191.573 bytes
更新:
有人问我使用的压缩算法。我通过 Deflater
和 DeflaterOutputStream
Java classes.
ZLIB
因此流程是:
- 示例数据 -> 将它们存储在
SQLite
- 从
SQLite
读取 -> 在内存中构建RequestPayload
对象 - 将
RequestPayload
对象转换为JSONObject
(使用自定义解析器)。 - 将
JSONObject
转换为字符串(使用JSONObject
toString()
方法) - 压缩字符串字节(使用
getBytes()
)-> 将其编码为 Base64 - 将最终的 Base64 字符串发送到服务器
编辑:
为了解决“可能的重复标志”问题,我在研究期间没有看到这个问题,因为它使用了 largeHeap
指令(我不使用),所以对我没有帮助。也没有公认的答案,唯一的答案实际上并没有提供实用的解决方案。
你的Qn很好。
您是否尝试过输入 AsyncTask
也许它会通过 Thread
处理来工作。
private String serialise() throws JSONException {
String sampleString = "";
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground( Void... voids ) {
// Serialise
sampleString = mPayload.parseJSON().toString(); // THIS LINE.
return null;
}
}.execute();
Logger.get().d(LOG_TAG, "Serialised payload: -> " + sampleString);
return sampleString;
}
我假设你有很好的知识。 抱歉,如果它可能不起作用。但它确实在我的情况下。请查收。
你会遇到巨大的Jsons转换问题,当转换为String时,系统会将全部内存分配给一个唯一变量,因此你会发生内存溢出。
最好的处理方法是进行一些 Json 流式传输,这将读取 Json 的部分内容并对其进行解析。
我认为重新发明轮子没有意义,所以我建议使用 Json 解析器库,例如 Gson 或 Jackson,它们会自动将您的 json 转换为 POJO,您可以存储它们在 SQLite 数据库中,您可以使用一些数据库辅助工具,例如 ORMLite。
这样就可以接收到一个巨大的JSON,直接将他解析成一个POJO对象,然后将POJO直接插入到数据库中,内存管理交给库
在这个site中你可以解析你JSON并以GSON或Jackson格式检索整个class,之后,你只需要添加ORMLite注释并创建数据库助手,真的很简单
很抱歉没有再提供任何反馈我一直忙于其他工作,这在我的优先队列中排在最后。
无论如何,真正对我有用的是使用 Google GSON
库。
我的连载方式变得简单了:
private String serialise() {
// Serialise
return mPayload.toStringFromGSON();
}
class Payload
中的方法 toStringFromGSON()
是:
public String toStringFromGSON() {
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
return gson.toJson(this);
}
总之我觉得这也算是临时补丁吧。事实上,我注意到应用程序使用的内存在这个阶段急剧增加,因此我认为这个问题只是被推迟了,最终会在更重的负载下发生..