【问题标题】:Memory usage for a specific list of object特定对象列表的内存使用情况
【发布时间】:2011-06-15 17:12:08
【问题描述】:

我有一个简单的 pojos(一个用户类)列表,其中包含大约 15 个简单字段和 1 个数组列表。 这些代表用户,其中可能有 100 或 1000 个将存储在内存中,以避免每次都从外部系统中检索它们。 (我正在使用 Ehcache)

我想通过 junit 测试了解这些用户的 K 个列表使用了多少内存。 我的直觉是,即使是 1000 个,像那些简单的 pojo 也没有任何威胁(换句话说,不到 100 Ko)

提前感谢您的回答。 非常感谢您的帮助。

【问题讨论】:

  • 为什么要使用 JUnit 测试来做这件事?这些值很可能取决于 JVM 版本/供应商/标志 CPU 架构。例如,Sun 的 JVM 在 64 位 Linux 上!= Sun 的 JVM 在 32 位 Linux 上!= Sun 的 JVM 在 64 位 Linux 上的 CompressedOops。您最好的方法是逐案分析。
  • 我知道这取决于很多事情,但我想对我自己的架构和我们在生产中使用的架构有一些真正的价值。我假设在其他架构上消耗 N 字节的 K 个简单用户可能不会多消耗 100 倍。

标签: java unit-testing memory-management


【解决方案1】:

JOL (Java Object Layout) 实用程序库,可以分析对象布局和内存占用。

为您的项目添加依赖项(例如使用 gradle compile 'org.openjdk.jol:jol-core:0.9'),然后您可以使用帮助类来打印或检查具体对象的内存占用。

// Print VM details
System.out.println(VM.current().details());

// Create new object (this can be your own data class)
Map<String, Long> o = new HashMap<>();
o.put("key1", 123L);

// To check object size (for example: from unit test)
System.out.println("Shallow size: " + VM.current().sizeOf(o));
System.out.println("Total size: " + GraphLayout.parseInstance(o).totalSize());
System.out.println();

// To print object details
System.out.println(ClassLayout.parseInstance(o).toPrintable());
System.out.println(GraphLayout.parseInstance(o).toPrintable());
System.out.println(GraphLayout.parseInstance(o).toFootprint());

此示例在Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)java version "1.8.0_191" 上的输出:

# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

Shallow size: 48
Total size: 232

java.util.HashMap object internals:
 OFFSET  SIZE                       TYPE DESCRIPTION                               VALUE
      0     4                            (object header)                           01 52 99 6a (00000001 01010010 10011001 01101010) (1788432897)
      4     4                            (object header)                           2d 00 00 00 (00101101 00000000 00000000 00000000) (45)
      8     4                            (object header)                           a3 37 00 f8 (10100011 00110111 00000000 11111000) (-134203485)
     12     4              java.util.Set AbstractMap.keySet                        null
     16     4       java.util.Collection AbstractMap.values                        null
     20     4                        int HashMap.size                              1
     24     4                        int HashMap.modCount                          1
     28     4                        int HashMap.threshold                         12
     32     4                      float HashMap.loadFactor                        0.75
     36     4   java.util.HashMap.Node[] HashMap.table                             [(object), null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]
     40     4              java.util.Set HashMap.entrySet                          null
     44     4                            (loss due to the next object alignment)
Instance size: 48 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

java.util.HashMap@2d6a9952d object externals:
          ADDRESS       SIZE TYPE                      PATH                           VALUE
        7410cb2b8         24 java.lang.Long            .table[0].value                123
        7410cb2d0    5383088 (something else)          (somewhere else)               (something else)
        7415ed680         48 java.util.HashMap                                        (object)
        7415ed6b0         24 java.lang.String          .table[0].key                  (object)
        7415ed6c8         24 [C                        .table[0].key.value            [k, e, y, 1]
        7415ed6e0         80 [Ljava.util.HashMap$Node; .table                         [(object), null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]
        7415ed730         32 java.util.HashMap$Node    .table[0]                      (object)


java.util.HashMap@2d6a9952d footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1        24        24   [C
         1        80        80   [Ljava.util.HashMap$Node;
         1        24        24   java.lang.Long
         1        24        24   java.lang.String
         1        48        48   java.util.HashMap
         1        32        32   java.util.HashMap$Node
         6                 232   (total)

【讨论】:

    【解决方案2】:

    如果您想要一个简单的测试,您可以将新尺寸设置为大并执行以下操作。 这仅在您的新大小远大于您正在创建的数据时才有效。 例如

    -XX:NewSize=1g -verbosegc

    如果您没有看到任何 GC,则该值将是正确的。

    long before = Runtime.getRuntime().freeMemory();
    
    //build object here
    
    long used = before - Runtime.getRuntime().freeMemory();
    

    注意:这假设您不生成临时对象。

    【讨论】:

      【解决方案3】:

      您可以在创建对象之前和之后计算 JRE 使用的内存,以便估计对象正在使用多少字节。

      System.gc();
      System.runFinalization();
      Thread.sleep(1000);
      long before = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
      
      //build object here
      
      System.gc();
      System.runFinalization();
      Thread.sleep(1000);
      long after = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
      
      long objectSize = after - before;
      

      【讨论】:

      • 非常感谢,Dogbane,这就是我正在寻找的解决方案。
      • 是的。 Thread.sleep 允许 GC 完成工作,因为它是异步的。
      • 简洁优雅!
      【解决方案4】:

      您可以将它们写入ByteOutputStream,然后获取字节数组并检查其长度。如果您的 pojo 是可序列化的,这将起作用。

      【讨论】:

      • 我发现这个解决方案很棒——直到我尝试过。我认为它给出了完全错误的、难以置信的结果(为什么......)。多么悲伤,听起来真好:´-(
      猜你喜欢
      • 2011-11-02
      • 2010-11-26
      • 2015-08-02
      • 2010-10-08
      • 2014-07-30
      • 2014-12-03
      • 2012-01-13
      • 1970-01-01
      相关资源
      最近更新 更多