Reputation: 25768
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
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
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
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
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
Reputation: 785866
There is an opensource java.SizeOf project that determines size of any of your Java object in memory.
Upvotes: 17