【问题标题】:java - memory usagejava - 内存使用
【发布时间】:2012-09-12 03:39:41
【问题描述】:

我正在开发一个加载大量数据(例如来自 csv)的应用程序。

我正在创建List<List<SimpleCell>> 并将读取的单元格加载到其中。 SimpleCell 类包含 5 * String,每个 String 平均有 10 个字符。

所以我在想,如果我读取 1000 行——每行包含 160 列——给出 1000*160=160 000 SimpleCell 的实例——大约是 160 000 * sizeof(SimpleCell.class) =~ 160 000 * 10 * 5 = 8 000 000 字节 =~ 7.63 MB。

但是当我查看 jconsole 时(并在单击 Perform GC 之后)内存使用量约为 790MB。这怎么可能?

注意,我不存储对任何“临时”对象的任何引用。 这是内存使用量上升时的代码:

        for(int i = r.getFromIndex(); i <= r.getToIndex(); ++i) {
            System.out.println("Processing: 'ZZ " + i + "'");
            List<SimpleCell> values = saxRead("ZT/ZZ " + i + "");
            rows.add(values);
        }

saxRead 只是创建 inputStream 用 SAX 解析它,关闭流并返回单元格(由 SAXHandler 创建) - 所以只有局部变量(我认为在不久的'未来'会被丢弃)。

我在读取 1000 行时收到 out of heap error,但我必须读取大约 7k。

显然 - 关于 jvm 内存,我不知道一些事情。 那么为什么在加载这么少的数据时内存使用量如此之大呢?

【问题讨论】:

  • 如果你创建一个 System.out.println ("Values size " + values.size() ),你是否有相当数量的 SimpleCells 实例?
  • 有什么原因你不能增量处理你的文件?
  • @HernanVelasquez values.size() 总是返回 160 - 在 SAXHandler 中有一个常数表示它。 @Wug 我正在逐步处理它 - saxRead 从中读取一个文件和一行。
  • 可以发一下saxRead方法吗?
  • @Xeon:所以...不要将它添加到列表中,处理它并删除它?

标签: java memory jvm heap-memory


【解决方案1】:

JVM 内存管理引入了很多开销。 例如,在 32 位 vm 上,一个 5 个字符的字符串会消耗 58 个字节的内存(不仅仅是 5 个!):

JVM 开销:16b + 簿记字段:12b + 指向 char[] 的指针:4b + char[] jvm 开销:16b + 数据:10b

【讨论】:

    【解决方案2】:

    使用 VisualVM 分析您的堆使用情况,并准备好大吃一惊。

    【讨论】:

      【解决方案3】:

      String 使用 48 字节加上文本大小 * 2。(每个字符为 2 字节)Simple Cell 对象使用 40 字节,它们的列表使用 1064 字节。

      这意味着每行使用 1064 + 160 * 40 + 5 * 180 * (48 + 20) 字节或大约 68K。如果您有 1000 行,您将使用大约 70 MB,这比您看到的要少得多。

      我建议您使用内存配置文件来查看到底有多少内存被什么使用。例如VisualVM 或 YourKit。

      根据您构建字符串的方式,您可以保留比这更多的内存。例如,您可能会保留对原始 XML 的引用,因为当您获取它的 substring 时,您实际上持有的是原始 XML 的副本。


      你可能会发现这个类很有用。如果字符串使用的内存超出了他们的需要,它将减少内存使用量,并使用固定大小的缓存来减少重复。

      static class StringCache {
          final WeakReference<String>[] strings;
          final int mask;
      
          @SuppressWarnings("unchecked")
          StringCache(int size) {
              int size2 = 128;
              while (size2 < size)
                  size2 *= 2;
              strings = new WeakReference[size2];
              mask = size2 - 1;
          }
      
          public String intern(String text) {
              if (text.length() == 0) return "";
      
              int hash = text.hashCode() & mask;
              WeakReference<String> wrs = strings[hash];
              if (wrs != null) {
                  String ret = wrs.get();
                  if (text.equals(ret))
                      return ret;
              }
              String ret = new String(text);
              strings[hash] = new WeakReference<String>(ret);
              return ret;
          }
      }
      

      【讨论】:

      • 假设SimpleCell extends Object。如果出于某种原因存在中间类,则权重会更大。这也是假设ArrayListLinkedList 的重量也会更大。
      • 我同意,OP 告诉我们的只是解释了大约 10% 的总内存使用量。
      • 正确 - 这是substring 问题。
      • 解决这个问题的方法是使用new String(string),这通常是没有意义的,但在这种情况下,可以确保字符串不会保持比它需要的更大的char[]。跨度>
      • 您可能会发现字符串缓存类有助于减少内存消耗,尤其是如果您有重复的字符串。
      【解决方案4】:

      Java 是 非常 非常需要内存的。考虑以下估计:

      32 位虚拟机:

      您的字符串之一的大小(大约)

      10 个 UTF-16 字符 = 20 个字节

      1 个数组长度 = 4 个字节

      1 个数组对象头 = 8 个字节

      1 个数组引用 = 4 个字节

      1 个偏移量、计数、哈希码(内部字段)= 12 个字节

      1 个对象头 = 8 个字节

      1 个典型的 Java 字符串 = 20 + 4 + 8 + 4 + 12 + 8 = 56 个字节

      简单单元格的大小(大约,包括字符串)

      5 个字符串 = 56 * 5 = 280 个字节

      5 个字符串引用 = 5 * 4 字节 = 20 字节

      1 个对象头 = 8 个字节

      1 SimpleCell = 180 + 20 + 8 = 308 字节

      160000 简单单元 = 308 * 160000 = 49280000 字节

      64 位 VM(无压缩 oops)

      您的字符串之一的大小(大约)

      10 个 UTF-16 字符 = 20 个字节

      1 个数组长度 = 4 个字节

      1 个数组对象头 = 8 个字节

      1 个数组引用 = 8 个字节

      1 个偏移量、计数、哈希码(内部字段)= 12 个字节

      1 个对象头 = 8 个字节

      1 个典型的 Java 字符串 = 20 + 4 + 8 + 8 + 12 + 8 = 60 个字节

      简单单元格的大小(大约,包括字符串)

      5 个字符串 = 60 * 5 = 300 个字节

      5 个字符串引用 = 5 * 8 字节 = 40 字节

      1 个对象头 = 8 个字节

      1 SimpleCell = 300 + 40 + 8 = 308 字节

      160000 简单单元 = 348 * 160000 = 55680000 字节

      显然与您的 790 Mb 相差甚远(看起来像泄漏),但几乎比您估计的多一个数量级。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-05-31
        • 2011-05-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多