Adam Lee
Adam Lee

Reputation: 25768

Is there a simple way to get the size of a java object?

I would like to know any simple way to retrieve the size of a java object? Also, anyway to get the size of a class like sizeof operator in c++?

Upvotes: 13

Views: 18393

Answers (5)

Stephen C
Stephen C

Reputation: 719386

The Instrumentation interface has a getObjectSize() method.

However this only gives you the size of the object itself, not its component subobjects. So for instance, it will tell you that all String objects are the same size.

Another problem is that the size of an object can actually change spontaneously. For instance, if you get the identity hashcode of an object and it survives a GC cycle, then its size will be increased by (at least) 4 bytes to store the identity hashcode value.


The problem of finding the size of "an object" is that it is impossible for a general utility class / method to know for sure where the abstraction boundaries of an arbitrary object are. There are problems for something even as simple as the String class. (Consider String objects created using substring(...) in Java 6. Do you could the char[] value object as a part of this, or part of the original String, or both? What does this mean for the sizes of the respective objects?)

Thus, it is inevitable that something like net.sourceforge.sizeof cannot give an entirely accurate accounting of space usage.

Upvotes: 8

user319609
user319609

Reputation: 11

It's definitely possible to get object size, 'cause an object of a certain class is just a fix-sized memory block. I wrote the following code to calculate the retained size of an object. It also provides a method like 'sizeof' in C++. It's ready-to-run and depends on nothing. You may copy and try it!

public class ObjectSizer {

    public static final Unsafe us = getUnsafe();

    public static boolean useCompressedOops = true;

    public static int retainedSize(Object obj) {
        return retainedSize(obj, new HashMap<Object, Object>());
    }

    private static int retainedSize(Object obj, HashMap<Object, Object> calculated) {
        try {
            if (obj == null)
                throw new NullPointerException();
            calculated.put(obj, obj);
            Class<?> cls = obj.getClass();
            if (cls.isArray()) {
                int arraysize = us.arrayBaseOffset(cls) + us.arrayIndexScale(cls) * Array.getLength(obj);
                if (!cls.getComponentType().isPrimitive()) {
                    Object[] arr = (Object[]) obj;
                    for (Object comp : arr) {
                        if (comp != null && !isCalculated(calculated, comp))
                            arraysize += retainedSize(comp, calculated);
                    }
                }
                return arraysize;
            } else {
                int objectsize = sizeof(cls);
                for (Field f : getAllNonStaticFields(obj.getClass())) {
                    Class<?> fcls = f.getType();
                    if (fcls.isPrimitive())
                        continue;
                    f.setAccessible(true);
                    Object ref = f.get(obj);
                    if (ref != null && !isCalculated(calculated, ref)) {
                        int referentSize = retainedSize(ref, calculated);
                        objectsize += referentSize;
                    }
                }
                return objectsize;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static int sizeof(Class<?> cls) {

        if (cls == null)
            throw new NullPointerException();

        if (cls.isArray())
            throw new IllegalArgumentException();

        if (cls.isPrimitive())
            return primsize(cls);

        int lastOffset = Integer.MIN_VALUE;
        Class<?> lastClass = null;

        for (Field f : getAllNonStaticFields(cls)) {
            if (Modifier.isStatic(f.getModifiers()))
                continue;

            int offset = (int) us.objectFieldOffset(f);
            if (offset > lastOffset) {
                lastOffset = offset;
                lastClass = f.getClass();
            }
        }
        if (lastOffset > 0)
            return modulo8(lastOffset + primsize(lastClass));
        else
            return 16;
    }

    private static Field[] getAllNonStaticFields(Class<?> cls) {
        if (cls == null)
            throw new NullPointerException();

        List<Field> fieldList = new ArrayList<Field>();
        while (cls != Object.class) {
            for (Field f : cls.getDeclaredFields()) {
                if (!Modifier.isStatic(f.getModifiers()))
                    fieldList.add(f);
            }
            cls = cls.getSuperclass();
        }
        Field[] fs = new Field[fieldList.size()];
        fieldList.toArray(fs);
        return fs;
    }

    private static boolean isCalculated(HashMap<Object, Object> calculated, Object test) {
        Object that = calculated.get(test);
        return that != null && that == test;
    }

    private static int primsize(Class<?> cls) {
        if (cls == byte.class)
            return 1;
        if (cls == boolean.class)
            return 1;
        if (cls == char.class)
            return 2;
        if (cls == short.class)
            return 2;
        if (cls == int.class)
            return 4;
        if (cls == float.class)
            return 4;
        if (cls == long.class)
            return 8;
        if (cls == double.class)
            return 8;
        else
            return useCompressedOops ? 4 : 8;
    }

    private static int modulo8(int value) {
        return (value & 0x7) > 0 ? (value & ~0x7) + 8 : value;
    }

    private static Unsafe getUnsafe() {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            return (Unsafe) f.get(Unsafe.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        System.out.println(retainedSize("Hello Leeeeeeeen"));
    }
}

Upvotes: 1

Miguel Gamboa
Miguel Gamboa

Reputation: 9393

Using the Unsafe class from the sun.misc you can get the fields offset. 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() { }
}

Upvotes: 3

Boann
Boann

Reputation: 50041

While your program is running, get another terminal window and run:

jmap -histo <process id of the java instance you want to debug>

In the output it gives the number and total size of object instances by class. E.g., 3 72 java.util.Date means there are 3 Date objects in memory taking 24 bytes each. You might need to pipe the output to a file or something if the relevant part scrolls away too fast.

Or, for (much) more detail, run:

jmap -dump:format=b,file=heap.bin <processid>
jhat heap.bin

Then open http://localhost:7000. You can browse through the objects on the heap in your browser.

More info:

http://docs.oracle.com/javase/6/docs/technotes/tools/share/jmap.html
http://docs.oracle.com/javase/6/docs/technotes/tools/share/jhat.html

I think it always gets rounded up to 8 by the way, on the Sun/Oracle JVM, even if the object is 12 bytes, it will take 16 in memory.

Upvotes: 1

anubhava
anubhava

Reputation: 785866

There is an opensource java.SizeOf project that determines size of any of your Java object in memory.

Upvotes: 17

Related Questions