当我基于JSON数组创建一个JSON对象时,出现内存不足的情况。

3

我使用Java创建了一个web服务来返回JSON数据。对于小数据没有问题,但当处理大数据时会出现内存溢出错误。

以下是我的代码:

public void getImages() throws Exception {
      JSONObject o = new JSONObject();
      JSONArray jsonArray = new JSONArray();

      try {
                Class.forName(DRIVERNAME);
                conn = DriverManager.getConnection(URL, USER, PASS);

                Statement s = connection.createStatement();
                ResultSet r = s.executeQuery("select * from images");

                while (r.next()) {
                          JSONObject obj = new JSONObject();

                          obj.put("id", r.getInt("id"));

                          byte[] iBytes = r.getBytes("image");
                          String iBase64 = DatatypeConverter.printBase64Binary(iBytes);
                          obj.put("image", iBase64);

                          jsonArray.put(obj);
                }

                o.put("images", jsonArray);

      } catch (SQLException e) {

      }
}

当向jsonArray中添加图像时,会出现内存不足的问题(java.lang.OutOfMemoryError: Java堆空间):

byte[] iBytes = r.getBytes("image");
String iBase64 = DatatypeConverter.printBase64Binary(iBytes);
obj.put("image", iBase64);

有什么办法可以解决这个问题吗?

要么不要这样做,要么增加可用内存的数量。 - Brian Roach
你通常在一次调用中检索多少图像,它们有多大?你当前的代码占用了相当多的内存,如果遇到OOM问题,则需要增加程序可用的内存量,或改进逻辑以减少尝试发送回客户端的数据量。 - Perception
你确定要通过 JSON 传输图像字节数据吗?也许如果你真的想这么做,可以设置图像大小限制或调整它们的大小。 - Christophe Roussy
@Perception 平均每次调用返回2000张图片。我需要将这些图片存储到应用程序移动端以供离线工作使用。但是,我不知道如何分页显示结果以避免内存溢出。你有什么想法吗? - Schlacter James
两千?这对于任何类型的应用程序来说都是很多了,更不用说移动应用了。你将会在两个方面遇到内存问题。根据平均图像大小(你没有提到),你肯定需要对结果集进行分页。 - Perception
显示剩余2条评论
2个回答

2
使用Jackson库中的流式JSON(ObjectMapper、JsonFactory、JsonGenerator)解决您的问题。请参见以下代码:
public void getImages() throws Exception {

    ObjectMapper objectMapper = new ObjectMapper();
    JsonFactory jf = objectMapper.getFactory();
    JsonGenerator jg = f.createGenerator(new File("c:\\images.json"), JsonEncoding.UTF8);

    try {
        Class.forName(DRIVERNAME);
        conn = DriverManager.getConnection(URL, USER, PASS);

        Statement s = connection.createStatement();
        ResultSet r = s.executeQuery("select * from images");

        jg.writeFieldName("images");
        jg.writeStartArray();

        while (r.next()) {
           jg.writeBinary(r.getBinaryStream("IMAGEM"), -1);
           in.close();
        }

        jg.writeEndArray(); 
        jg.writeEndObject(); 
        jg.close();

    } catch (SQLException e) {
        //use the exceptions
    }
}

这里的“in”变量是什么?你还忘记写StartObject()了。 - cdietschrun

1
为什么不直接返回图片的URL并让浏览器拉取该资源呢?我不太明白通过JSON响应传递它的优势,因为这要求Java在发送之前将图像存储在内存中。

我做不到因为移动应用程序将在离线状态下工作。因此,用户的移动应用程序无法访问互联网以请求 URL。 - Schlacter James
在这种情况下,您需要配置堆大小更大。 - 75inchpianist
我已经注释了创建图像的代码行,解决了内存不足的问题。 - Schlacter James

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