Java Objects

    Impressed by habrahabr.ru/blogs/java/134102 .

    Recently, I had to dig a bit inside the JVM. Pretty interesting experience. The text in the aforementioned topic does not quite coincide 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 utility class Unsafe. Description class brazenly stolen from wasm.ru .
    The little-known class sun.misc.Unsafe has been included with the Sun Java Runtime since the first versions. Like all other classes in package sun. *, Unsafe is not documented, but the names (mostly native) of the functions visible during decompilation speak for themselves. There are obviously functions for working with memory (allocateMemory, freeMemory, ...), reading and writing values ​​to a given address (putLong, getLong, ...), and some more specialized ones (throwException, monitorEnter, ...).

    But, it’s 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 through 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 also the internal variable theUnsafe, which can be accessed using Reflection. I put together a small helper 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 operations of taking an object by address and taking an address from an object: o2a (Object o) and a2o (long address).
    Now you can watch something.

    Object structure


    Consider 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.

    Its 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 . It stores various information, for example, about locks. More details in the comments.
    88 D7 79 32 - looking ahead, I’ll say that this is a reference to a class object.
    02 00 00 00 is the value of our primitive field of type int.
    D0 7C E4 22 - again, looking ahead, I will say that this is a reference to an object of a field of type Integer.
    And further ... further this is another object lying nearby in memory. By changing our code a bit, you can verify this.

    
    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 a field of type Integer, and then took the object lying at this address. The result confirmed our assumptions.

    The following example will confirm our expectations regarding the second block (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();
            v.a = 5;
            v.b = 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 values ​​of the fields. And, as a result, the objects got into memory nearby, 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.

    Offset example:
    
    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 an empty block of 4 bytes follows the reference to the value of a field of type Integer. All objects are padded to a multiple of 8 bytes.

    Offset example:
    
    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 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
    ...
    


    conclusions


    Java object consists of:
    4 bytes - magic mask.
    4 bytes - class address.
    4 bytes is the size of the array (only in the case of arrays, of course).
    n * 4 bytes - for each field of the object (value of a primitive type or a reference to the object).
    All this is complemented to a factor of 8 bytes.

    He wrote in a hurry. Corrections, questions, suggestions and rays of hatred are welcome.

    Also popular now: