在gson.fromJson()中出现了内存不足异常。

11

我使用以下代码将Json字符串(strWebserviceResult)转换为我的对象:

EntMyClass entMyClass = gson.fromJson(strWebserviceResult,EntMyClass.class);

strWebserviceResult很大(约为2.5 MB)时,我在Android手机设备上会遇到内存不足的异常,而在具有更大内存的平板电脑上则没有出现过。

我该如何解决这个问题?

有人有什么建议吗?

05-26 15:52:49.607: E/dalvikvm-heap(2078): Out of memory on a 9200-byte allocation.
05-26 15:52:49.618: E/dalvikvm(2078): Out of memory: Heap Size=31879KB, Allocated=27693KB, Bitmap Size=936KB, Limit=32768KB
05-26 15:52:49.618: E/dalvikvm(2078): Extra info: Footprint=31879KB, Allowed Footprint=31879KB, Trimmed=7400KB
05-26 15:52:49.618: E/AndroidRuntime(2078): FATAL EXCEPTION: Thread-19
05-26 15:52:49.618: E/AndroidRuntime(2078): java.lang.OutOfMemoryError: (Heap Size=31879KB, Allocated=27693KB, Bitmap Size=936KB)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at java.util.ArrayList.add(ArrayList.java:123)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter.deserialize(DefaultTypeAdapters.java:664)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter.deserialize(DefaultTypeAdapters.java:624)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.JsonDeserializerExceptionWrapper.deserialize(JsonDeserializerExceptionWrapper.java:51)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:92)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler(JsonObjectDeserializationVisitor.java:117)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.ReflectingFieldNavigator.visitFieldsReflectively(ReflectingFieldNavigator.java:63)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:120)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.JsonDeserializationContextDefault.fromJsonObject(JsonDeserializationContextDefault.java:76)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:54)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter.deserialize(DefaultTypeAdapters.java:663)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter.deserialize(DefaultTypeAdapters.java:624)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.JsonDeserializerExceptionWrapper.deserialize(JsonDeserializerExceptionWrapper.java:51)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:92)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.JsonDeserializationVisitor.visitUsingCustomHandler(JsonDeserializationVisitor.java:80)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:101)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.JsonDeserializationContextDefault.fromJsonArray(JsonDeserializationContextDefault.java:67)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:52)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.Gson.fromJson(Gson.java:551)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.Gson.fromJson(Gson.java:498)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.Gson.fromJson(Gson.java:467)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.Gson.fromJson(Gson.java:417)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at com.google.gson.Gson.fromJson(Gson.java:389)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at org.mabna.order.businessLayer.BoWebService.getDataForUpdate(BoWebService.java:188)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at org.mabna.order.ui.ActToolDataExchange.threadGetDataForFullUpdate(ActToolDataExchange.java:371)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at org.mabna.order.ui.ActToolDataExchange.access$9(ActToolDataExchange.java:362)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at org.mabna.order.ui.ActToolDataExchange$33.run(ActToolDataExchange.java:603)
05-26 15:52:49.618: E/AndroidRuntime(2078):     at org.mabna.order.utils.Utilities$5.run(Utilities.java:778)

2
也许这篇文章能帮到你。 - Praveenkumar
4个回答

6
与另一篇帖子类似,我想问一下是否有任何方法可以避免您的应用程序使用大量内存。如果能够做到这一点,那么它将是最优解决方案。如果您的应用确实需要那么多内存,可以尝试在清单文件中为您的应用设置android:largeHeap="true"。这是参考文献:http://developer.android.com/reference/android/R.styleable.html#AndroidManifestApplication_largeHeap 观看此视频以了解更多信息:http://www.youtube.com/watch?v=_CruQY55HOk

8
不建议使用此解决方案,因为它会增加垃圾回收时间(需要处理更多的内存)。 - ol_v_er
1
这不是正确的解决方案。增加内存只会延迟OOM,而不能解决它。 - Stefano Giacone

6
尝试使用接受JsonReader输入的fromJson方法,这样您就不需要一次性将整个输入字符串保存在内存中。以下是一些示例代码:fromJson。请注意,保留Html标记。
        final HttpURLConnection c = (HttpURLConnection) url.openConnection();
        final InputStreamReader isr = new InputStreamReader(c.getInputStream());
        final JsonReader reader = new JsonReader(isr);
        final EntMyClass entMyClass = GSON.fromJson(reader, EntMyClass.class);
        reader.close();
        c.disconnect();

我的初始数据是一个加密字符串。我将其解密为字符串,得到了strWebserviceResult作为字符串。如何将strWebserviceResult转换为“InputStream”? - Bobs
加密字符串 -> 清除的字符串 WebserviceResult作为JSON字符串 -> 我的对象 - Bobs
也许你可以先将解密后的字符串存储到本地文件中?然后你应该能够从本地文件获取一个InputStream。 - Joe

3
首先,您真的需要将2.5MB的数据加载到内存中吗?首先创建了一个大字符串,然后解析它(另一个对象在内存中),然后创建了一个巨大的EntMyClass实例。这种方法效率真的很低。
我猜你在这个对象中有一个List。根据你提供的信息,我建议将数据流式传输到数据库中。然后,您可以创建适配器以在ListView中显示数据。
有很多方法可以做到这一点。我使用Jackson库是因为它是最快的,GSON应该有类似的功能。
假设您在此对象中有一个列表,则以下是将json数组存储在数据库中的示例:
    //create parser
    final JsonParser jsonParser = objectMapper.getJsonFactory().createJsonParser(inputStream);

    JsonToken jsonToken = jsonParser.nextToken();
    while ((jsonToken = jsonParser.nextToken()) != null && jsonToken != JsonToken.END_ARRAY) {
        //map json object to ItemClass
        final ItemClass item = jsonParser.readValueAs(ItemClass.class);
        //store in database
        itemDAO.insert(item);
    }

如果将其包裹在数据库事务中,不仅当出现错误时数据可以回滚,而且效率也更高。


答案就是我想要的。 - zhangle

0
请阅读gson作者的回答-这里 另外,我也想发表一下我的看法:
        JsonReader r = null;
        try {
            Reader reader = new BufferedReader(new InputStreamReader(is));
            r = new JsonReader(reader);
            result = gson.fromJson(r, clazz);
        } finally {
            if (null != r) {
                r.close();
            }
        }

谢谢Dmitry。我该如何将我的字符串转换成InputStream(is)? - Bobs
你正在使用哪种加密/解密算法来处理这个字符串?为什么我会问这个问题呢?原因是你需要按小块而不是整个2.5MB组织处理这个字符串,换句话说 - 部分处理。 - Dmitry Polishuk
public String getClearedResponse(String decryptionPassword) throws Exception { String zippedString = Crypto.decrypt(this.content, decryptionPassword); return Utilities.decompress(zippedString); } - Bobs
它还有解压过程 :( - Bobs
实际上,应该是这样的:public InputStream getClearedResponse(String decryptionPassword) throws Exception {return new BufferedInputStream(new GZIPInputStream(Crypto.decrypt(is, decryptionPassword)));}。而且在你的Crypto.decrypt中,你需要实现你的CryptoReader。这是你自己的加密算法吗? - Dmitry Polishuk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接