为什么我的jsonParser使用Streaming API会占用更多内存

时间:2014-12-22 18:25:02

标签: java json jackson

我有jsonparse整个文件的代码:

ConcurrentHashMap<String, ValueClass<V>> space = new ConcurrentHashMap<String, ValueClass<V>> ();
Map<String, ValueClass<V>> map = mapper.readValue(new FileReader(fileName)...);
for each entry of map:
      space.put(entry.getKey(), entry.value());

用于解析文件的内存很大,远远超过文件本身的大小。为了节省内存,我决定用jsonparse流API替换代码,如下所示:

            ConcurrentHashMap<String, ValueClass<V>> space = new ConcurrentHashMap<String, ValueClass<V>> ();
            JsonFactory f = new MappingJsonFactory();
            JsonParser jp = f.createJsonParser(new File(fileName);
            JsonToken current;
            current = jp.nextToken();
            if (current != JsonToken.START_OBJECT) {
                show error and return;
            }
            ObjectMapper mapper = new ObjectMapper();
      while (jp.nextToken() != JsonToken.END_OBJECT) {
           key = jp.getCurrentName();
           current = jp.nextToken();
           if (key != null) {
               if (current == JsonToken.START_OBJECT) {
                   mem_before=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
                       value = mapper.readValue(jp, ValueClass.class); (1)
                   mem_after=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
                   mem_diff = mem_after - mem_before; (2)
                       space.put(key, value); (3)
               }
               else jp.skipChildren();
           else jp.skipChildren();
      }
然而,它比一次解析整个文件花费的内存要多得多。并且它显示内存增加是由于(1)(我使用Runtime.getRuntime()检测assignedMemory.totalMemory() - Runtime.getRuntime()。freeMemory before(1)之前和之后(1)获得差异)

根据{{​​3}}和其他文章所说,

不应该使用流API节省内存?

修改

@StaxMan:谢谢你回答我。看起来你对我们应该期待的内存消耗非常熟悉。

我的目的是将内容从文件加载到内存,这样我就不需要从文件中搜索内容。所以我确实需要地图中的所有内容。

我认为流式传输可能有所帮助的原因是:如果将整个文件作为一个对象加载,我猜除了为map变量消耗内存:space之外,我们需要一个大的额外内存来解析整个文件,这是一个很大的负担;但是通过使用流式传输,虽然我们仍然需要相同的内存用于map变量:space,我们只需要少量的额外内存来解析文件的每个条目,这个额外的内存很小,因为我们重用它来解析每个条目而不是解析整个文件。它不节省内存吗?如果我错了,请纠正我。

我知道内存大小可能超过文件大小。但首先我不明白为什么它需要比文件更多(在我的情况下是文件大小的2.7倍);第二,我不明白为什么使用流媒体我会花更多的内存(甚至不使用流媒体的两倍)。内存泄漏?但我没有看到我的代码有任何问题。本来应该至少不超过2.7次。

此外,您是否知道如何估计对象的内存消耗,例如我的地图变量空间的每个条目?

1 个答案:

答案 0 :(得分:0)

如果您解释了您想要实现的目标,那将会有所帮助。例如,您是否绝对需要内存中的所有值?或者是否可以一次处理一个值,并避免使用所有键值构建Map

如果没有解释你想要在那里做什么,你的第二次尝试没有多大意义;但基本上使用的内存量应该是值POJO,密钥和包含它们的Map的空间。这可能不仅仅是输入JSON或更少,但它实际上取决于您正在处理的内容类型。例如,字符串将占用JVM中的内存内存而不是JSON文件:UTF-8中的大多数字符都是单字节,而JVM中的每个char都是16位值(UCS-2编码)。此外,虽然JSON字符串有3或4字节开销(引号,分隔符),但java.lang.String的内存开销更像是每个字符串16或24个字节。

类似地,Map消耗了相当多的内存,用于JSON文件不需要的结构。 JSON每个条目有几个分隔符,可能是6到8个字节(取决于使用缩进)。另一方面,除了String键(具有上述开销)和POJO值之外,Java Maps还有更大的每个条目开销(可能是16个字节?)。

所以:总而言之,内存中的对象可能比JSON文件本身包含的内存消耗更多的内存并不罕见。