将大型 JSON (InputStream) 放入 String 时出现内存不足错误

Posted

技术标签:

【中文标题】将大型 JSON (InputStream) 放入 String 时出现内存不足错误【英文标题】:Out of memory error when putting large JSON (InputStream) to String 【发布时间】:2011-08-16 01:53:53 【问题描述】:

我从 Web 服务接收 gzip 后的 JSON,然后将其解压缩(解压缩后的 JSON 大小为 3.2MB)。 我需要将收到的 InputStream 转换为 String,然后我可以创建 JSONObject 并解析它。我用这段代码来做:

public static String InputStreamToString(InputStream in) 
    throws IOException 

    BufferedInputStream bis = new BufferedInputStream(in);
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
    int result = bis.read();

    while(result != -1) 
      byte b = (byte)result;
      buf.write(b);
      result = bis.read();
            
    return buf.toString();

我在最后一行收到 java.lang.OutOfMemoryError:“return buf.toString();”在具有 288MB Ram 的模拟器和设备上。

我该怎么办?

【问题讨论】:

【参考方案1】:

一次读取一个字节是所以 1990 年代。要么使用 HttpClient 和 BasicResponseHandler,要么至少使用 read the data in respectable chunks 并使用 StringBuilder 附加它们。

假设您仍然遇到问题,问题是没有单个内存块足以容纳您的字符串,基于您的应用程序一直在做的其他事情。 android 垃圾收集器不是压缩收集器,因此可能有大量可用堆空间但不足以满足特定的分配请求。

在这种情况下,您可能需要切换到某种流式 JSON 解析器。如果您碰巧只针对 Honeycomb 或更高版本,则可以使用 JSONReader。否则,据报道Jackson 可以在 Android 上运行,并且显然具有流媒体模式。

【讨论】:

我尝试以“可敬的块”读取数据,但错误仍然存​​在。我不知道如何使用 BasicResponseHandler 因为 WS 响应是 gzip 压缩的。有可能吗? @DixieFlatline:你可以试试svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/… @CommonsWare:我一直在寻找如何实现 GZip 压缩,而那个类可以解决问题!谢谢! @CommonsWare:我尝试将 ResponseInterceptor(在您的链接中)添加到我的 httpClient 对象,但 Android 找不到 GzipDecompressingEntity,所以我从 javasourcecode.org/html/open-source/httpcomponents-httpcore/… 向我的项目添加了一个类。一切都可以编译,但它不会解压缩响应。如果您想让问题更清楚,我可以为我的问题添加更多来源。 @DixieFlatline:您最好发布一个单独的问题,标记为httpclientandroid,关于在Android 上使用HttpClient 处理GZIP 压缩。我自己还不需要处理 GZIP 的东西。而且,再次记住,这很可能无法解决您的问题,所以我不会在前面投入大量时间 - 查看流式 JSON 解析器并让解析工作。一次读取超过一个字节主要是一种性能优化,可能对您的内存问题有益。【参考方案2】:

您可以尝试使用

创建一个新的 JSONObject
new JSONObject(new JSONTokener(in)) 

而不是直接转换成字符串。但是,这可能只会延迟问题。如果您没有足够的内存来将 3.2 meg 的字符串加载到内存中,那么您可能没有足够的内存来将其加载为 json 对象,这将比简单字符串占用更多的内存。

【讨论】:

JSONTokener 在构造函数 AFAICT 中采用 String,而不是 InputStream json.org/javadoc/org/json/… Android 的JSONTokener 副本没有该构造函数。而且,您不能替换 Android 的 JSONTokener 副本,除非使用 jarjarorg.json 完全重新打包到其他命名空间中。

以上是关于将大型 JSON (InputStream) 放入 String 时出现内存不足错误的主要内容,如果未能解决你的问题,请参考以下文章

如何将可下载文件放入HttpServletResponse?

在 java 中使用 Connect Direct 传输 inputStream

最佳实践 - Json 解析中的字符串与 InputStream(使用 gson)

如何将 Struts 2 动作类中的 InputStream 值传递给 JSP 页面中的 Ajax 并将该值转换为 JSON 数组

将 JSON 放入数组

将字节数组放入 JSON,反之亦然