I came across an interview question:
class Test {
int a ;
int b;
char c;
}
How much memory object of this class will take and why when implemented on:
a) 32-bit computer
b) 64-bit computer
I got the answer as:
For 32-bit: 4+4+2+8 = 18 bytes
For 64-bit: 4+4+2+16 = 26 bytes
Due to some extra memory allocated which is 8 bytes in 32-bit system and 16 bytes in 64-bit system, in addition to the normal size of object.
Can you please give some explanation for this statement.
P.S.: I also want to share an answer I got from another source (can't rely upon, wanted to verify):
In 32 bit pc object occupies 8 bytes more than the actual obj size as defined by its data members ..... and in 64 bit pc object occupies 16 bytes more than actual object size as defined by its data members .... now the ques arises why it happens ... the reason behind it as far as i know is ::: in 32 bit pc first 8 bytes are reserved by JVM for referring to class definitions, alignment ,spaces for super class and sub classes etc .. and for 64 bit it reserves 16 bytes for these .. There is a formula for calculating how much heap space is required by an object when it is created and it is calculated as ..... . . Shallow Heap Size = [reference to the class definition] + space for super class fields + space for instance fields + [alignment]
How do you calculate how much memory does the object “itself” require? Apparently there is a formula for it:
Shallow Heap Size = [reference to the class definition] + space for super class fields + space for instance fields + [alignment]
Does not seem too helpful, eh? Let’s try to apply the formula using the following sample code:
class X {
int a;
byte b;
java.lang.Integer c = new java.lang.Integer();
}
class Y extends X {
java.util.List d;
java.util.Date e;
}
Now, the question we strive to answer is – how much shallow heap size does an instance of a Y require? Lets start calculating it, assuming that we are on a 32-bit x86 architecture:
As a starting point – Y is a subclass of X, so its size includes “something” from the super class. Thus, before calculating the size of Y, we look into calculating the shallow size of X.
Jumping into the calculations on X, first 8 bytes are used to refer its class definition. This reference is always present in all Java objects and is used by JVM to define the memory layout of the following state. It also has three instance variables – an int, an Integer and a byte. Those instance variables require heap as follows:
a byte is what it is supposed to be. 1 byte in a memory. an int in our 32 bit architecture requires 4 bytes. a reference to the Integer requires also 4 bytes. Note that when calculating retained heap, we should also take into account the size of a primitive wrapped into the Integer object, but as we are calculating shallow heap here, we only use the reference size of 4 bytes in our calculations. So – is that it? Shallow heap of X = 8 bytes from reference to the class definition + 1 byte (the byte) + 4 bytes (the int) + 4 bytes (reference to the Integer) = 17 bytes? In fact – no. What now comes into play is called alignment (also called padding). It means that the JVM allocates the memory in multiples of 8 bytes, so instead of 17 bytes we would allocate 24 bytes if we would create an instance of X.
If you could follow us until here, good, but now we try to get things even more complex. We are NOT creating an instance of X, but an instance of Y. What this means is – we can deduct the 8 bytes from the reference to the class definition and the alignment. It might not be too obvious at first place but – did you note that while calculating the shallow size of X we did not take into account that it also extends java.lang.Object as all classes do even if you do not explicitly state it in your source code? We do not have to take into account the header sizes of superclasses, because JVM is smart enough to check it from the class definitions itself, instead of having to copy it into the object headers all the time.
The same goes for alignment – when creating an object you only align once, not at the boundaries of superclass/subclass definitions. So we are safe to say that when creating a subclass to X you will only inherit 9 bytes from the instance variables.
Finally we can jump to the initial task and start calculating the size of Y. As we saw, we have already lost 9 bytes to the superclass fields. Let’s see what will be added when we actually construct an instance of Y.
Y’s headers referring to its class definition consume 8 bytes. The same as with previous ones. The Date is a reference to an object. 4 bytes. Easy. The List is a reference to a collection. Again 4 bytes. Trivial. So in addition to the 9 bytes from the superclass we have 8 bytes from the header, 2×4 bytes from the two references (the List and the Date). The total shallow size for the instance of Y would be 25 bytes, which get aligned to 32.
In 32-bit JVM we can have less memory for heap size than in 64-bit JVM. In 64-bit JVM we can specify more memory for heap size than in 32-bit JVM. The limit for maximum memory in 32-bit is useful for 4G connectivity.
2.1. Minimum object size is 16 bytes for modern 64-bit JDK since the object has 12-byte header, padded to a multiple of 8 bytes.
For 64 bit platforms and Java stacks in general, the recommended Maximum Heap range for WebSphere Application Server, would be between (4096M - 8192M) or (4G - 8G).
As noted in the comments, this is the most significant reason for the size difference. 64-bit memory addresses are twice as long as 32-bit memory addresses, so 64-bit pointers are also twice as long. 64-bit programs that make heavy use of pointers will be noticeably larger than their 32-bit counterparts.
Depends on JVM. As to HotSpot JVM, the correct answer is:
I would answer such an interview Question as follows:
It is not specified ...
It potentially depends on the Java implementation (i.e. the Java release, vendor, and target instruction set / architecture) as well as 32 versus 64 bit.
The object consists of the fields (whose alignment is platform specific) with an object header (whose size is platform specific) and the size is then rounded up to a platform-specific heap object size granularity.
A better approach is to measure the object size (e.g. using TLAB on a modern HotSpot JVM).
While the object sizes may vary, the meaning of int
and char
does not. An int
is always 32 bit signed, and a char
is always 16 bit unsigned.
If the interviewer told you this:
For 32-bit: 4+4+2+8 = 18 bytes
That probably means an int
and int
and a char
plus 2 32-bit words of object header. However, I think that he is wrong. In fact the 2
should probably be a 4
because I believe that fields are typically stored as 4 (or 8) bytes aligned on a 32-bit word boundary
For 64-bit: 4+4+2+16 = 26 bytes
As above, but with 2 64-bit words of object header. Again, I don't think this is correct.
Due to some extra memory allocated which is 8 bytes in 32-bit system and 16 bytes in 64-bit system, in addition to the normal size of object.
The granularity of heap allocation is some multiple 2 words.
But I would stress that this is not necessarily correct. Certainly, nothing in the official JVM specs require objects to be represented like that.
To get accurate accounting you can turn off the TLAB. There is still the risk of a GC changignt eh result, but it is much less hit and miss.
public class Main {
static class Test {
int a ;
int b;
char c;
}
public static void main(String sdf[]) throws Exception {
// warmup
new Test();
long start = memoryUsed();
Object o = new Test();
long used = memoryUsed() - start;
System.out.printf("Memory used for %s, was %,d bytes%n", o.getClass(), used);
}
public static long memoryUsed() {
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}
}
Note: for large tests, it can help if you increase the Eden size to 1 GB or more.
Memory used for class Main$Test, was 24 bytes
For 64-bit: 4+4+2+16 = 26 bytes
This is not possible as the HotSpot JVM only ever allocates multiples of 8 bytes. BTW You can increase the minimum allocation unit in Java 8 to 16 or 32 bytes.
The header is 12 bytes so the size is
4 + 4 + 2 + 12 (header) + 2 (padding to 8 byte multiple) = 24
Run with -XX:ObjectAlignmentInBytes=16
you see
Memory used for class Main$Test, was 32 bytes
This raises the question, why would you do this if it wastes memory?
The benefit relates to compressed oops which allows you to use 32-bit references in a 64-bit JVM. Normally compressed oops can address 32 GB because it knows all objects are aligned by 8 bytes i.e. you can address 4 G * 8 bytes. However if you increase the byte alignment to 16, you can address 4G * 16 bytes or 64 GB using compressed oops. I believe you can use a 32 byte alignment, but this wastes more memory than it saves in most cases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With