I've searched about the size of a java object for a long time, there are a lot of answers like this, everyone is telling me that the size of the overhead of a java object, and how to calculate out the actual size. But how do they know that? I did not find any evidence from the official oracle documents. What is the evidence for that conclusion? Or the data was just come from some guesses based on some experiments?
Another thing. It is mentioned in the official document that there is a 'approximative' way to measure the object - the Instrumentation way, can anybody explain to me what is the 'approximately' means? When it is accurate, when it is not. Better to have the evidence.
how to calculate out the actual size. But how do they know that?
from experience.
I did not find any evidence from the official oracle documents.
Its up to the JVM. For OpenJDK based JVMs, 32-bit JVM has a different header size to 64-bit JVM as the header contains a reference. Other JVMs could be different again.
Or the data was just come from some guesses based on some experiments?
Essentially, yes.
can anybody explain to me what is the 'approximately' means?
When you measure the size of an object it can means many different things
- How big is the object? (Shallow depth)
- How much memory does it use? (Object allocation is 8-byte aligned i.e. always a multiple of 8)
- How much space does the object and all the objects referenced use? (Deep depth)
- How much space might be freed if it were discarded? (how many objects are shared and does it appear in the middle of two fragments of freed memory)
- Do you count the space used on the stack, or space used in off heap memory?
Given you can have many difference answers depending on what you need to know, it is useful to have one number which is approximately close to all of these which you use for calculations.
Where you get a problem using Runtime is that the TLAB allocated data in large blocks. These large blocks can be further allocated in a multi-thread way. The downside is you don't get accurate memory used information.
static long memTaken() {
final Runtime rt = Runtime.getRuntime();
return rt.totalMemory() - rt.freeMemory();
}
public static void main(String... args) {
long used1 = memTaken();
Float i = new Float(0);
long used2 = memTaken();
System.out.println("new Float(0) used "+(used2 - used1)+" bytes.");
}
run without options
new Float(0) used 0 bytes.
Turn off the TLAB and you see with -XX:-UseTLAB
new Float(0) used 336 bytes.
This is much higher than you might expect because the class itself had to be loaded. If you create one instance of a Float first by adding to the start
Float j = new Float(1);
you get
new Float(0) used 16 bytes
I think the answer can only be empirical as it is implementation dependent:
The Java virtual machine does not mandate any particular internal structure for objects.
For me the best method is the most down-to-earth one, using
long memTaken() {
final Runtime rt = Runtime.getRuntime();
return rt.totalMemory() - rt.freeMemory();
}
- Remember initial
memTaken()
;
- Make an array of a million object (adapt this number to fit your heap);
- Subtract
memTaken()
from the remembered one.
There may be some transient allocation in the process, so you also need to run the GC. There are no guarantees provided by System.gc()
, but this approach has always worked for me:
for (int i = 0; i < 3; i++) { System.gc(); Thread.sleep(50); }
You must be careful to ensure this gives stable results. For example, an approach is to calculate memory load for several different object counts and compare results. They should all match on the memory/instance answer they give.
I have advised this before, been put down for the lame solution, been recommended to use proper tooling, etc. So I used tooling, for example jvisualvm
—and it gave wrong results. This method has never given wrong results to me.
there are two different sizes for java objects: the shallow size and the retained size.
the shallow size of an object is the sum of sizes of all it's fields: straight-forward for primitive members, and the size of a pointer for every non-primitive member(and this varies between 32 & 64 bit architectures). you can find the shallow size of an object at runtime using instrumantation. here is a nice tutorial
retained size is defined by the heap space that will be freed when the object is garbaged.
finding the retained size of an object is not trivial, mainly because objects are usually composed of other objects. for example:
public class Clazz {
public byte[] member;
public static void main(String[] args) {
byte[] bytes = new byte[128];
Clazz a = new Clazz();
Clazz b = new Clazz();
a.member = bytes;
b.member = bytes;
}
}
what is the retained size of a? and b?
use a profiler to compute retained size (most profilers are using static heap analysis for that).