6

i have a list of simples pojos (a User class) with about 15 simple fields & 1 arrayList. Those represent users & maybe 100 or 1000 of them will be store in memory in order to avoid to retrieve them from external system every time. (i'm using Ehcache)

I would like to know with a junit test how much memory is used by a list of K of those users. I have the intuition that simple pojo like those one even for a 1000 ones are not threatening in any way (in other words less than 100 Ko)

Thanks in advance for your anwser. i really appreciate your help.

skaffman
  • 398,947
  • 96
  • 818
  • 769
Omar Elfada
  • 394
  • 3
  • 14
  • Why would you want to do this with a JUnit test? These values are most likely JVM version/vendor/flag _and_ CPU-architecture dependant. For isntance, Sun's JVM on 64bit Linux != Sun's JVM on 32bit Linux != Sun's JVM on 64bit Linux with CompressedOops. Your best best is a per-case analysis. – Robert Munteanu Jan 26 '11 at 09:12
  • Also see http://stackoverflow.com/questions/2062553/java-object-creation-and-memory-size – Robert Munteanu Jan 26 '11 at 09:12
  • I know that it depends on many things but i would like to have some real value on my own architecture & one we are using in production. I assume that K simple users consuming N bytes may not consume 100 times more on an other architecture. – Omar Elfada Jan 26 '11 at 10:07

4 Answers4

17

You can calculate the memory used by the JRE before and after you create your object, in order to approximate how many bytes are being used by your object.

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
  • 266,786
  • 75
  • 396
  • 414
4

There is JOL (Java Object Layout) utility library that can analyze object layout and memory footprint.

Add dependency to your project (for example with gradle compile 'org.openjdk.jol:jol-core:0.9') and then you can use helper classes to print or check memory footprint of concrete object.

// 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());

The output of this example on 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)
Ruslan Stelmachenko
  • 4,987
  • 2
  • 36
  • 51
2

If you want a simple test, you can set the new size to be large and do the following. This only works if your new size is much larger than the data you are creating. e.g.

-XX:NewSize=1g -verbosegc

The value will be correct provided you don't see any GC.

long before = Runtime.getRuntime().freeMemory();

//build object here

long used = before - Runtime.getRuntime().freeMemory();

Note: this assumes you don't generate an temporary objects.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
0

You can write them to ByteOutputStream, then get byte array and check its length. This will work if your pojos are Serializable.

AlexR
  • 114,158
  • 16
  • 130
  • 208
  • I found that solution brilliant - until I tried it. I think it gives totally wrong, implausible results (whyever...). How sad, it sounded so nice :´-( – cupiqi09 Jun 19 '18 at 09:48