【问题标题】:File size vs. in memory size in JavaJava中的文件大小与内存大小
【发布时间】:2013-05-19 17:02:25
【问题描述】:

如果我在磁盘上获取大约 2kB 的 XML 文件,并将内容作为字符串加载到 Java 中的内存中,然后测量对象大小约为 33kB

为什么尺寸会大幅增加?
如果我在 C++ 中做同样的事情,内存中的结果字符串对象更接近 2kB。

为了测量 Java 中的内存,我使用了Instrumentation。 对于 C++,我取序列化对象的长度(例如字符串)。

【问题讨论】:

  • 你是如何测量内存大小的?
  • 你如何将它存储在java中的内存中。此外,Java 的每个对象的开销约为 16 个字节,因此如果您有很多小字符串对象,您的开销将非常高!!!
  • 我期望和开销,但不是~30kB
  • @imrichardcole 您能否发布您用于测量内存大小的 java/c++ 代码。在不知道您是否首先正确测量它们的情况下,任何人都无法回答这个问题
  • 你能描述一下你想出 33KB 的方法吗?我相信您找到的大小可能不是字符串本身的大小。

标签: java c++ memory


【解决方案1】:

我认为涉及多个因素。 首先,正如 Bruce Martin 所说,java 中的对象每个对象有 16 个字节的开销,而 c++ 没有。 其次,Java 中的字符串可能是每个字符 2 个字节,而不是 1 个。 第三,Java 为其字符串保留的内存可能比 C++ 的 std::string 多。

请注意,这些只是可能产生巨大差异的想法。

【讨论】:

  • 我相信我们都知道这些开销。但是,它应该是字符串长度的两倍左右(或者如果有很多字符需要代理,则为 x3/x4)。但是,它无法解释 >15 倍的差异。还有其他问题
  • 我相信,如果 java 实现使用许多单个字符串对象来存储其数据,这些开销会自行放大。
【解决方案2】:

假设您的 XML 文件主要包含 ASCII 字符并使用将它们表示为单个字节的编码,那么您可以预计内存大小至少是两倍,因为 Java 在内部使用 UTF-16(我听说过一些试图优化这一点的 JVM,尽管如此)。除此之外,还有 2 个对象(String 实例和一个内部 char 数组)的开销,其中包含一些字段,IIRC 总共大约 40 个字节。

因此,除非您使用的是奇怪的 JVM,否则您的 33kb 的“对象大小”肯定是不正确的。你测量它的方法一定有问题。

【讨论】:

  • 是的,对象大小应该四舍五入到 8 IIRC。
  • @michael - 让我检查一下我测量尺寸的实现
【解决方案3】:

在Java String 对象中有一些额外的数据,这会增加它的大小。
它是对象数据、数组数据和其他一些变量。这可以是数组引用、偏移量、长度等。

访问http://www.javamex.com/tutorials/memory/string_memory_usage.shtml了解详情。

【讨论】:

  • 然而,对于 2KB(ASCII 格式)/4KB(UTF-16 格式)字符串,这样的额外数据不会花费近 30KB
  • 阿德里安,你是对的。这么说是错误的。您可以轻松拥有庞大的数据结构,其中存储 0 个有用数据。
  • java.lang.String 包含对数组的引用、偏移量、长度和哈希码作为整数,以及另外 2 个引用。对于 x86 jvm,它将产生 24 个字节,对于 x64,它将产生 36b。此外,还有一些 char 数组的内存开销。
  • @Chechulin 我们都知道这一点。然而,这个开销只是几十个字节。即使我们包括了 ASCII 与 UTF16 的开销,也只是大小的两倍,这意味着它应该花费大约 4KB。 33KB 无疑处于这种开销无法解释的水平。
  • @Chechulin 我在谈论 Java :) 我们谈论的所有“正常”开销将花费 ~40 字节。如果我们将 ASCII 与 UTF16 之间的差异视为“开销”,它的大小通常会翻倍。但是,OP 要求在内存中花费 33KB 的 ~2000-char-string,这似乎无法用 Java String 的正常开销来解释
【解决方案4】:

String:String 的内存增长跟踪其内部 char 数组的增长。但是,String 类又增加了 24 个字节 的开销。 对于大小不超过 10 个字符的非空字符串,相对于有用负载(每个字符 2 个字节加上长度 4 个字节)的额外开销成本在 100% 到 400% 之间。

更多: What is the memory consumption of an object in Java?

【讨论】:

    【解决方案5】:

    是的,你应该 GC 并给它时间来完成。只是 System.gc();并在循环中打印 totalMem()。您还最好在数组中创建一百万个字符串副本(测量空数组的大小,然后用字符串填充),以确保您测量的是字符串的大小,而不是您的程序中可能存在的其他服务对象的大小。单独的字符串不能占用 32 kb。但是 XML 对象的层次结构可以。

    话虽如此,我无法抗拒在 Java 世界中没有人关心内存(和缓存命中)的讽刺意味。我们知道 JIT 正在改进,在某些情况下它可以胜过原生 C++ 代码。因此,无需担心内存优化。前期优化是万恶之源。

    【讨论】:

    • JIT 可能是一个有效点。只是好奇,在什么情况下 JIT 会认为创建一个大数组会对应用程序有益?
    • 谁告诉你大阵列的?我知道 JVM 更喜欢很多小对象,每个都消耗大量内存。
    • arrr... 我只是在想 JIT 可能对 String 对象进行“优化”以使其变得如此之大的唯​​一部分就是其中的 char 数组。也许我应该问,“只是好奇,在什么情况下 JIT 会认为为小对象分配更大的内存对应用程序有益?”
    • 我不知道,但是黑客说a bare Object takes up 8 bytes; an instance of a class with a single boolean field takes up 16 bytes: 8 bytes of header, 1 byte for the boolean and 7 bytes of "padding" to make the size up to a multiple of 8; 64位jvm的情况应该更糟。
    【解决方案6】:

    如其他答案所述,Java 的 String 增加了开销。如果您需要在内存中存储大量字符串,我建议您将它们存储为 byte[] 代替。这样做内存中的大小应该与磁盘上的大小相同。

    字符串->字节[]:

    String a = "hello";
    byte[] aBytes = a.getBytes();
    

    字节[] -> 字符串:

    String b = new String(aBytes);
    

    【讨论】:

      猜你喜欢
      • 2022-11-29
      • 2011-11-01
      • 2023-03-31
      • 2017-11-10
      • 1970-01-01
      • 2016-07-03
      • 1970-01-01
      • 1970-01-01
      • 2014-01-29
      相关资源
      最近更新 更多