Java:即使在使用GSON流API后仍出现OutOfMemory错误

qvtsj1bj  于 2022-11-06  发布在  Java
关注(0)|答案(1)|浏览(232)

我一直在处理一个问题语句,其中我们有一个巨大的JSON响应进入,当我们使用传统的gson解析技术解析它时,它通常会给予OutOfMemoryException,因为此方法在处理数据之前将其存储在内存中,因此作为对此问题的解决方案,我一直在处理JSON响应流,它不会将所有内容都放入内存中。所以它一直工作得很好,直到大约160万张唱片,在那之后甚至坏了。所以这是我们得到的例外。
线程“main”中出现异常java.lang.内存不足错误:Java堆空间
这是我用来做这件事的全部代码:

// Getting reponse into InputStream and casting it to JsonReader object for parsing
InputStream liInStream = luURLConn.getInputStream();
lCycleTimeReader = new JsonReader(new InputStreamReader(liInStream, "UTF-8"));

我们的JSON如下所示:

{
"Report_Entry": [
    {
       "key1": "value",
       "key2": "value",
       "key3": "value",
       "key4": "value",
       "key5": "value"
    },
    {
       "key1": "value",
       "key2": "value",
       "key3": "value",
       "key4": "value",
       "key5": "value"
    }
]}

使用这个对象进入我们的解析方法:

public HashMap<String, HashMap<String, String>> getcycleTimeMap(JsonReader poJSONReaderObj,
        CycleTimeConstant cycleTimeConstant, int processId) {

    Integer counter = 0;
    HashMap<String, HashMap<String, String>> cycleTimeMap = new HashMap<String, HashMap<String, String>>();
    HashMap<String, HashMap<String, String>> finalcycleTimeMap = new HashMap<String, HashMap<String, String>>();

    try {
        CycleTime cycleTime = new CycleTime();

        poJSONReaderObj.beginObject();
        while (poJSONReaderObj.hasNext()) {

            String name = poJSONReaderObj.nextName();
            if (name.equals("Report_Entry")) {
                poJSONReaderObj.beginArray();
                while (poJSONReaderObj.hasNext()) {

                    JsonToken nextToken2 = poJSONReaderObj.peek();
                    if (JsonToken.BEGIN_OBJECT.equals(nextToken2)) {
                        poJSONReaderObj.beginObject();
                    } else if (JsonToken.END_OBJECT.equals(nextToken2)) {
                        poJSONReaderObj.endObject();
                    } else {
                        String nextString = "";
                        if (JsonToken.STRING.equals(nextToken2)) {
                            nextString = poJSONReaderObj.nextString();
                        } else if (JsonToken.NAME.equals(nextToken2)) {
                            nextString = poJSONReaderObj.nextName();
                        }

                        switch (nextString) {
                        case "key1":
                            cycleTime.setKey1(poJSONReaderObj.nextString());
                            break;
                        case "key2":
                            cycleTime.setKey2(poJSONReaderObj.nextString());
                            break;
                        case "key3":
                            cycleTime.setKey3(poJSONReaderObj.nextString());
                            break;
                        case "key4":
                            cycleTime.setKey4(poJSONReaderObj.nextString());
                            break;
                        case "key5":
                            cycleTime.setKey5(poJSONReaderObj.nextString());
                            break;
                        }
                    }

                    poJSONReaderObj.endObject();

                    System.out
                            .println("Value of Map is : " + new Gson().toJson(cycleTime) + "counter  : " + counter);
                    counter++;
                    System.out.println("Counter : " + counter);
                    cycleTimeMap = (HashMap<String, HashMap<String, String>>) cycleTimeBpProcessIterator(
                        cycleTime, cycleTimeConstant, counter, processId);
                }
                finalcycleTimeMap.putAll(cycleTimeMap);
            }
        }
        JsonToken nextToken = poJSONReaderObj.peek();
        if (JsonToken.END_OBJECT.equals(nextToken)) {
            poJSONReaderObj.endObject();
        } else if (JsonToken.END_ARRAY.equals(nextToken)) {
            poJSONReaderObj.endArray();
        }
    } catch (IOException ioException) {
        ioException.printStackTrace();
    }

    System.out.println("FINAL MAP TO BE LOADED : " + new Gson().toJson(finalcycleTimeMap));

    return finalcycleTimeMap;

}

用于处理响应的POJO类:

public class CycleTime {

    private String key1 = "";
    private String key2 = "";
    private String key3 = "";
    private String key4 = "";
    private String key5 = "";

    public String getKey1() {
        return key1;
    }
    public void setKey1(String key1) {
        this.key1 = key1;
    }
    public String getKey2() {
        return key2;
    }
    public void setKey2(String key2) {
        this.key2 = key2;
    }
    public String getKey3() {
        return key3;
    }
    public void setKey3(String key3) {
        this.key3 = key3;
    }
    public String getKey4() {
        return key4;
    }
    public void setKey4(String key4) {
        this.key4 = key4;
    }
    public String getKey5() {
        return key5;
    }
    public void setKey5(String key5) {
        this.key5 = key5;
    }

}

我不知道什么可能是这里的罪魁祸首,但似乎它是给同样的错误,我想知道什么应该是下一个方法,以避免这个内存溢出异常。

jpfvwuh4

jpfvwuh41#

将整个文档读入一个对象并不意味着流式读取会对您有所帮助。此外,Gson使用流式读取是因为它只是一种可选的读取和写入方式。然而,您的方法远远不够好:

  • 桂生事:
  • 最主要的是:我完全正确地使用了Gson,让它完成它的工作。我无法运行您提供的JSON文档的代码:它既不适用于根JSON对象,也不适用于唯一的顶层对象条目(因此,由于不正确地使用hasNextbeginObject/endObject对,您的反序列化器被破坏了)。
  • 常见的Java内容:
  • 不要在返回一个部分组成的对象的过程中捕捉异常(正确吗?);
  • 不使用Throwable.printStackTrace(使用适当的日志记录设备);
  • 如果您不想使用记录器,则将其打印到System.err(这只是用于此目的的适当标准流);
  • Integer作为计数器是个坏主意,因为它会创建许多装箱值,尤其是对于大型文档(使用int--它就很好);
  • 可以(并且应该)检查enum值是否与==相等(这是安全的,因为它们是单例);
  • 然后,您还可以使用switch来形成枚举(两者都比较短,编译时更安全);
  • 不要在循环中创建Gson示例,特别是在有很多迭代的循环中(Gson示例是不可变的,因为它是线程安全的,但在构造它的对象时并不便宜);
  • 不要在可以静态类型化普通对象的地方使用map(这是好的);
  • 返回总是一个键值对Map的目的是什么;(返回值);
  • 常见的设计:
  • 尽可能使用通用类型进行声明:不是HashMap,而是Map(如果有一天你需要另一个带有有序键的map怎么办?或者如果你根本不需要map怎么办?);
  • 反向依赖关系(如果不需要CycleTime和5个键怎么办?);
  • 流媒体内容:
  • 如果它在OOM错误中运行,那么收集一个明显不适合你的应用RAM的巨大Map有什么意义呢?(使用回调或承诺(推方法)来处理单个元素,使用迭代器或流(拉方法),使用React流(推方法),等等);
  • 只收集小内存占用量的结果或使用聚合(否则您将面临出现OOM的风险)。

这就是通过回调使用推送方法来减少内存占用的方法:
第一个
如您所见,在上面的运行程序中,您可以选择您喜欢的处理输入的方式:或者是一个哑日志记录,或者是一个简单计数器,或者是一个简单的collect-to操作。
有关通过Stream方法的拉动方法,请参见:https://stackoverflow.com/a/69282822/12232870

相关问题