180

I want to record how much memory (in bytes, hopefully) an object takes up for a project (I'm comparing sizes of data structures) and it seems like there is no method to do this in Java. Supposedly, C/C++ has sizeOf() method, but this is nonexistant in Java. I tried recording the free memory in the JVM with Runtime.getRuntime().freeMemory() before and after creating the object and then recording the difference, but it would only give 0 or 131304, and nothing in between, regardless of the number of elements in the structure. Help please!

posdef
  • 6,498
  • 11
  • 46
  • 94
Tyler Petrochko
  • 2,611
  • 4
  • 24
  • 30
  • 6
    http://stackoverflow.com/questions/52353/in-java-what-is-the-best-way-to-determine-the-size-of-an-object – Sergey Grinev Feb 20 '12 at 21:38
  • 1
    You may want to try the totalMemory() and then freeMemory() and subtract those instead. – Matt Feb 20 '12 at 21:40
  • 5
    I recommend you taking a look at Jbellis JAMM. (https://github.com/jbellis/jamm) It relies on the instrumentation agent mentioned above but encapsulates some hepful utilities to deep measure the footprint of an entire object-graph. Looks simple and pretty cool to me. – Claudio Feb 26 '13 at 14:17
  • it will work only for primitive structures, not for structures with collections or something. Size of int[].class can be few bytes, but size of new int[1e9] will be few gigabytes. – Mikhail Ionkin Aug 21 '22 at 10:33

3 Answers3

99

You can use the java.lang.instrumentation package.

It has a method that can be used to get the implementation specific approximation of object size, as well as overhead associated with the object.

The answer that Sergey linked has a great example, which I'll repost here, but you should have already looked at from his comment:

import java.lang.instrument.Instrumentation;

public class ObjectSizeFetcher {
    private static Instrumentation instrumentation;

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    public static long getObjectSize(Object o) {
        return instrumentation.getObjectSize(o);
    }
}

Use getObjectSize:

public class C {
    private int x;
    private int y;

    public static void main(String [] args) {
        System.out.println(ObjectSizeFetcher.getObjectSize(new C()));
    }
}

Source

Neuron
  • 5,141
  • 5
  • 38
  • 59
Hunter McMillen
  • 59,865
  • 24
  • 119
  • 170
  • 1
    Says "The only way to access an instance of the Instrumentation interface is for the JVM to be launched in a way that indicates the agent class - see the package specification. The Instrumentation instance is passed to the premain method of the agent class. Once an agent acquires the Instrumentation instance, the agent may call methods on the instance at any time. " <-- how do I do that (in Eclipse)? – Tyler Petrochko Feb 20 '12 at 21:47
  • Don't I have to specify how the JVM launches? I tried it and it keeps giving me NullPointerException – Tyler Petrochko Feb 20 '12 at 21:54
  • 2
    Edit your post above with what you tried. – Hunter McMillen Feb 21 '12 at 00:11
  • 180
    Don't know how this answer has gotten so many upvotes. It completely ignores the fact that it is necessary to compile an agent class to pass to the JVM as an argument. Not doing so will render instrumentation null, and trigger a NullPointerException. Here's the proper answer: http://stackoverflow.com/questions/52353/in-java-what-is-the-best-way-to-determine-the-size-of-an-object – arkon Aug 16 '15 at 10:50
  • There must be something not quite right with this approach when trying to find my web session size: it reports 40, while ObjectGraphMeasurer (https://github.com/DimitrisAndreou/memory-measurer) reports "Footprint{Objects=34, References=52, Primitives=[float, int x 19, char x 257, long x 2, byte x 68557]}". ObjectGraphMeasurer is clearly the winner. – Yuci Sep 02 '16 at 11:03
  • Is the long value the number of bytes ? – Anirudh Kashyap Oct 25 '18 at 20:41
93

Look into https://github.com/DimitrisAndreou/memory-measurer.
Guava uses it internally, and ObjectGraphMeasurer is especially straightforward to use out-of-the-box, without any special command-line arguments.

import objectexplorer.ObjectGraphMeasurer;

public class Measurer {

  public static void main(String[] args) {
    Set<Integer> hashset = new HashSet<Integer>();
    Random random = new Random();
    int n = 10000;
    for (int i = 1; i <= n; i++) {
      hashset.add(random.nextInt());
    }
    System.out.println(ObjectGraphMeasurer.measure(hashset));
  }
}
Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • This was pretty helpful. Even the other one was very easy to make it work. Have you used this before? – Venki Apr 23 '12 at 18:24
  • 2
    I've used memory-measurer before, yes. (I contribute to Guava, and I did mention that we use it internally. ;) ) – Louis Wasserman Apr 23 '12 at 18:27
  • Instant response. that's why i love SO. thanks a ton. I am trying to use it to measure up a memory activity. it has come in very handy. – Venki Apr 23 '12 at 18:35
  • 8
    I suspect that the most frequent SO users come here to procrastinate -- so of course we're on here _all the time,_ and we respond to comments instantly. – Louis Wasserman Apr 23 '12 at 18:46
  • @LouisWasserman if I am getting an output of Footprint{Objects=40004, References=92773, Primitives=[int x 40004, float]}, what is the total size? – Ani Jun 16 '14 at 22:50
  • 1
    That varies depending on your JVM. If you want to measure bytes, then you should be using `MemoryMeasurer` from the same project; follow the instructions there. – Louis Wasserman Jun 16 '14 at 23:31
  • @LouisWasserman If I use 64 bit JVM, can you tell me how to do the calculations? – Ani Jun 17 '14 at 15:31
  • You need to know if CompressedOops is on, but in that case you should assume an Object has a 12 byte overhead, and that references, ints, and floats each take 4 bytes. – Louis Wasserman Jun 17 '14 at 15:44
  • Very helpful. But this jar has a dependency of guava. It's not a executable jar :( – kamushin Dec 16 '15 at 08:21
  • @kamushin not sure why that matters? – Louis Wasserman Dec 16 '15 at 08:24
  • @LouisWasserman emmmm..I added all jars in lib into my build path, but when I run my server, I got `java.lang.NoClassDefFoundError: com/google/common/base/Predicates`. So I have to add `guava` dependency in my `pom.xml`. It seems `guava-r09.jar` does't work. – kamushin Dec 16 '15 at 08:31
  • So you have to add the dependency? So what? – Louis Wasserman Dec 16 '15 at 08:35
  • @LouisWasserman I can't find any file describe the dependency. – kamushin Dec 16 '15 at 08:42
  • That sounds like a problem with your maven setup? But you can download the Guava jar from the github site. – Louis Wasserman Dec 16 '15 at 08:44
  • @LouisWasserman I download `memory-measurer` jar from github. I didn't know I can get `memory-measurer` from maven. – kamushin Dec 16 '15 at 08:49
  • What is the complexity of the operation measure? – M4rk Mar 08 '16 at 19:57
  • 1
    @rodi Linear in the result, but with a pretty high constant factor due to reflection. – Louis Wasserman Mar 08 '16 at 20:03
  • O(N bytes * reflection constant) you mean? – M4rk Mar 08 '16 at 20:11
  • @rodi Yes, that's what I had in mind. – Louis Wasserman Mar 08 '16 at 20:11
  • I mean, it's also O(# objects in the object graph), which is somewhat tighter I suppose? For e.g. an int array it'll be O(1). – Louis Wasserman Mar 08 '16 at 20:23
  • I went with JAMM per the answer below, you can get it from Ivy/SBT and it does not require Guava (which I often avoid from scala). Just my 2 cents. – Daniel Langdon May 26 '16 at 15:58
  • Wonder if I should use this version instead https://github.com/msteindorfer/memory-measurer as DimitrisAndreou's version is not maintained any more? – Yuci Sep 02 '16 at 11:06
  • is this available in maven central? I can't seem to get the library from maven. is there any private distribution for this package? – ahrooran Mar 11 '23 at 17:57
  • memory-measurer jar not working. I passed jar like this: -javaagent:D:\company\javaagents\object-explorer.jar – ahrooran Mar 11 '23 at 18:27
  • Error message says Instrumentation is not setup properly. You have to pass -javaagent:path/to/object-explorer.jar to the java interpreter – ahrooran Mar 11 '23 at 18:27
19

The java.lang.instrument.Instrumentation class provides a nice way to get the size of a Java Object, but it requires you to define a premain and run your program with a java agent. This is very boring when you do not need any agent and then you have to provide a dummy Jar agent to your application.

So I got an alternative solution using the Unsafe class from the sun.misc. So, considering the objects heap alignment according to the processor architecture and calculating the maximum field offset, you can measure the size of a Java Object. In the example below I use an auxiliary class UtilUnsafe to get a reference to the sun.misc.Unsafe object.

private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model"));
private static final int BYTE = 8;
private static final int WORD = NR_BITS/BYTE;
private static final int MIN_SIZE = 16; 

public static int sizeOf(Class src){
    //
    // Get the instance fields of src class
    // 
    List<Field> instanceFields = new LinkedList<Field>();
    do{
        if(src == Object.class) return MIN_SIZE;
        for (Field f : src.getDeclaredFields()) {
            if((f.getModifiers() & Modifier.STATIC) == 0){
                instanceFields.add(f);
            }
        }
        src = src.getSuperclass();
    }while(instanceFields.isEmpty());
    //
    // Get the field with the maximum offset
    //  
    long maxOffset = 0;
    for (Field f : instanceFields) {
        long offset = UtilUnsafe.UNSAFE.objectFieldOffset(f);
        if(offset > maxOffset) maxOffset = offset; 
    }
    return  (((int)maxOffset/WORD) + 1)*WORD; 
}
class UtilUnsafe {
    public static final sun.misc.Unsafe UNSAFE;

    static {
        Object theUnsafe = null;
        Exception exception = null;
        try {
            Class<?> uc = Class.forName("sun.misc.Unsafe");
            Field f = uc.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            theUnsafe = f.get(uc);
        } catch (Exception e) { exception = e; }
        UNSAFE = (sun.misc.Unsafe) theUnsafe;
        if (UNSAFE == null) throw new Error("Could not obtain access to sun.misc.Unsafe", exception);
    }
    private UtilUnsafe() { }
}
Miguel Gamboa
  • 8,855
  • 7
  • 47
  • 94
  • 1
    I'm not sure about this: ((int)maxOffset/WORD) + 1)*WORD, but I think it does work as I'm pretty sure 64 bit variables are never crosssing an 8 byte boundary in a 64-bit VM. Good code otherwise, although you could of course get rid of the whole map making effort. I made my own version which also handles object arrays here: http://tinybrain.de/1011517 (would still need to add primitive arrays). – Stefan Reich Oct 30 '17 at 22:27