-
Notifications
You must be signed in to change notification settings - Fork 26
Object creation
Objects in Java are created by an instantiation process[0], which predictably creates an instance of the object. That instance contains a header and a table of instance fields (that is, non-local, non-static fields).
Several events trigger instantiation;
- the NEW bytecode is generated by the compiler (e.g. from use of Java's
newkeyword)[1] - an object is created by a call to its constructor
- a static class is referred to for the first time
- a call to the
clone()method is made on a cloneable object - an array is created via
newor a declaration that has an implicitnew(e.g.,int[] myNumbers = {10, 20, 30};) - a primitive is boxed[2]
Instantiation consists of creating the object with an object header[3] and then creating the field table for the object's instance fields. Other data, such as the methods and their bytecodes, metadata about the class, etc.[4], is not stored in the instance, but rather in the method area[5]--where it was placed by the classloader.
Only in item #2 above is the constructor called as part of the object creation. But even then, the object is first instantiated and then a call is made to the constructor. (Note that between the instantiation and the call to the constructor, any static initialization blocks[6] are executed.)
[0] Principally handled in Jacobin JVM by jvm\instantiate.go
[1] The NEW bytecode is generated by the Java compiler for these instantiation actions:
- Whenever you write code such as:
new MyClass() - Anonymous inner classes
- non-static inner classes
- enum constants
Note: the Java compiler does not emit a NEW bytecode for creation of these objects:
- arrays (uses a different bytecodes that depend on the array type)
- string literals (uses LDC)
- reflection (uses INVOKEVIRTUAL)
- autoboxing (uses a call to INVOKESTATIC)
- cloning (uses INVOKEVIRTUAL)
- deserialization (uses INVOKEVIRTUAL)
[2] Boxing (aka autoboxing) the conversion that the Java compiler performs to transform a primitive type (like int, boolean, or double) into its corresponding object wrapper class (like Integer, Boolean, or Double). This feature was introduced in Java 5 to bridge the gap between Java's primitive system and its object-oriented system, specifically to allow primitives to be used in Generics and the Collections Framework (e.g., ArrayList<Integer>) without requiring manual conversion code.
[3] The object header and field layouts in Jacobin JVM are at the top of object\object.go
[4] In Jacobin JVM, this metadata is stored in a struct called ClData inside a large struct, Klass, both of which are defined in classloader\classes.go
[5] Despite its name, the JVM's method area contains much more than methods. In fact, its primary role is to hold metadata for all classes. In Jacobin, the method area is defined here
[6] static initialization blocks are chunks of Java code placed between {{double curly braces}}. They are mandatorily the first code executed and are run before any constructor code. These blocks are highly limited in what it can do and are mostly used to initialize static fields. For example, they cannot access instance fields.