Under the impression of
habrahabr.ru/blogs/java/134102 .
Recently I had to dig a little inside the JVM. Quite an interesting experience. The text in the above topic does not quite agree with my experience, but I do not consider myself the owner of absolute truth. Below I will share with readers a small part of my experiments that relate directly to Java objects.
Test system
Windows XP SP3 32bit
D: \ dev \ puzzles> java -version
java version "1.6.0_29"
Java (TM) SE Runtime Environment (build 1.6.0_29-b11)
Java HotSpot (TM) Client VM (build 20.4-b02, mixed mode, sharing)
Training
All experiments were conducted using the utilitarian class Unsafe. Class description brazenly stolen from
wasm.ru.The little-known sun.misc.Unsafe class has been included in the Sun Java Runtime package since the first versions. Like all the other classes in package sun. *, Unsafe is not documented, but the names (most of which are native) functions that are visible during decompilation speak for themselves. There are explicit functions for working with memory (allocateMemory, freeMemory, ...), reading and writing values at a given address (putLong, getLong, ...) and some more specialized (throwException, monitorEnter, ...).
')
But, it is not so easy to instantiate Unsafe. The only constructor is private, and in getUnsafe () the loader of the calling class is checked and the object is returned only if the class is loaded via the Bootloader. Otherwise, we get a SecurityException.
public static Unsafe getUnsafe() { Class class1 = Reflection.getCallerClass(2); if (class1.getClassLoader() != null) throw new SecurityException("Unsafe"); else return theUnsafe; }
Fortunately, there is an internal variable theUnsafe, which can be reached using Reflection. I collected a small
auxiliary class. import java.lang.reflect.Field; import sun.misc.Unsafe; public class T { public static Unsafe u; private static long fieldOffset; private static T instance = new T(); private Object obj; static { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); u = (Unsafe) f.get(null); fieldOffset = u.objectFieldOffset(T.class.getDeclaredField("obj")); } catch (Exception ex) { throw new RuntimeException(ex); } }; public synchronized static long o2a(Object o) { instance.obj = o; return u.getLong(instance, fieldOffset); } public synchronized static Object a2o(long address) { u.putLong(instance, fieldOffset, address); return instance.obj; } public static Unsafe get() { return u; } }
We will need the operation of taking the object to the address and taking the address from the object: o2a (Object o) and a2o (long address).
Now you can watch something.
Object structure
Consider the operation of the following code:
import sun.misc.Unsafe; public class V { private Integer b = 3; private int a = 2; public static void main(String[] argv) throws Exception { Unsafe u = T.get(); long obj = T.o2a(new V()); for (int i = 0; i < 28; i++) System.out.print(u.getByte(obj + i) + " "); } }
This code is a class containing 2 private fields (int and Integer). We create an instance of this class, find out its address in memory, and output 28 bytes.
His result in my case was as follows (I formatted the output in groups of 4 bytes for clarity):
01 00 00 00 88 D7 79 32 02 00 00 00 D0 7C E4 22 01 00 00 00 E8 09 19 37 68 6F E4 22
So, what we see:
01 00 00 00 is the so-called
magic . This is an object
mask . Stores various information, such as locks. More in the comments.
88 D7 79 32 - looking ahead, I will say that this is a link to a class object.
02 00 00 00 is the value of our primitive int field.
D0 7C E4 22 - again, looking ahead, I will say that this is a reference to an object of type Integer field.
And then ... next is another object lying next to the memory. By slightly changing our code, you can check it out.
import sun.misc.Unsafe; public class V { private int a = 2; private Integer b = 777; public static void main(String[] argv) throws Exception { Unsafe u = T.get(); long obj = T.o2a(new V()); for (int i = 0; i < 28; i++) System.out.print(u.getByte(obj + i) + " "); long field = u.getAddress(obj + 3 * 4); Object i = T.a2o(field); System.out.print("\nInteger: " + i); } }
Result:
01 00 00 00 98 D6 79 32 02 00 00 00 C8 FB E4 22 ... Integer: 777
In this example, we took the address of an Integer field, and then took an object lying at that address. The result confirmed our assumptions.
The following example confirms our expectations regarding the second unit (class address).
import sun.misc.Unsafe; public class V { private int a = 2; private int b = 3; public static void main(String[] argv) throws Exception { Unsafe u = T.get(); long obj = T.o2a(new V()); V v = new V(); va = 5; vb = 6; for (int i = 0; i < 32; i++) System.out.print(u.getByte(obj + i) + " "); } }
Result:
01 00 00 00 08 D6 79 32 02 00 00 00 03 00 00 00 01 00 00 00 08 D6 79 32 05 00 00 00 06 00 00 00
In this example, I removed the reference fields from the class. Created 2 objects, in one of which I changed the field values. And, as a result, objects got into the memory side by side, and they can be distinguished by the value of primitive fields.
Another example with an array:
import sun.misc.Unsafe; public class V { public static void main(String[] argv) throws Exception { Unsafe u = T.get(); long obj = T.o2a(new Integer[] {1, 2, 3}); for (int i = 0; i < 28; i++) System.out.print(u.getByte(obj + i) + " "); } }
01 00 00 00 B8 C9 79 32 03 00 00 00 40 78 E4 22 50 78 E4 22 60 78 E4 22 ...
Arrays really store the number of elements in it.
Example with offset:
import sun.misc.Unsafe; public class V { private Integer a = 1; public static void main(String[] argv) throws Exception { Unsafe u = T.get(); long obj = T.o2a(new V()); for (int i = 0; i < 28; i++) System.out.print(u.getByte(obj + i) + " "); } }
Here I left the class with only one field.
Result:
01 00 00 00 B0 D7 79 32 60 78 E4 22 00 00 00 00 01 00 00 00 E8 09 19 37 ...
Here you can see that for the link to the value of the field of type Integer is an empty block of 4 bytes. All objects are supplemented to a size multiple of 8 bytes.
Example with offset:
import sun.misc.Unsafe; public class V { private int a = 2; private int b = 3; public static void main(String[] argv) throws Exception { Unsafe u = T.get(); V v = new V(); synchronized (v) { long obj = T.o2a(v); for (int i = 0; i < 32; i++) System.out.print(T.hex(u.getByte(obj + i)) + " "); } } }
Here the object in question is located in the synchronization block, as a result, the value of the first 4 bytes has changed.
Result:
8C 84 F0 00 58 D6 79 32 02 00 00 00 03 00 00 00 ...
findings
A Java object consists of:
4 bytes - magic mask.
4 bytes - class address.
4 bytes - the size of the array (only in the case of arrays, of course).
n * 4 bytes - for each field of the object (the value of a primitive type or object reference).
All this is complemented to a factor of 8 bytes.
He wrote in a hurry. Corrections, questions, suggestions
and rays of hate are welcome.