In the last post we had a brief idea about the memory model of the Java Virtual Machine. We also came across certain terms like the Creation, Linking, Loading and Initialization of classes. In this post we will try to improve our understanding of all these terms.
At the Java Virtual Machine Startup
The JVM starts up by creating an initial class and loads it using the bootstrap class loader (we will learn about the class loader in another post). Some implementation of the JVM allow us to pass the name of the initial class using the command line.
The JVM then links this initial class. Linking in itself is a very complex process which has many steps like verification, preparation, resolution, access control, method overriding.
After linking, the JVM initializes the class and invokes the method with the signature public static void main(String args) . The main method may result in linking other classes and in turn execution of additional methods as well.
Now let us discuss all these steps in detail
Creating & Loading Classes
Creation of a class C means creating the internal representation of the class in the JVM. This is done in the method area (discussed in the previous post). The internal representation is specific to implementation of the JVM.
A class is loaded by a class loader, the loading process is a two step process where the first step is to create an array of bytes representing the ClassFile structure. The second step is to define the class using the defineClass method which derives the class from the array of bytes.
We will discuss more about class loaders and their types in a different post. But this is the step where you get most of the ClassNotFoundException or NoClassDefFoundError
This is the process which takes place after the class loading, it involves verifying and preparing the class or interface and its direct parents(direct superclass or direct superinterface). It may also include verifying and preparing the element type if the class is an array type. There are scenarios when linking can result in creation of new data structures as one class can be dependent on another classes. There is a possibility of getting an OutOfMemoryError.
The JVM enforces structural constraints which must be satisfied by a class or interface. The process of verification ensures that the binary representation of the class or interface does satisfy the structural constraint.
This may also initiate loading other classes of interest but the new classes may not get verified as a part of the verification of this class.
As a part of the verification process, there is a chance of getting the VerifyError in case the structural constraints are not satisfied.
This phase involves creation of static fields of a class or interface and initializing them with their default values. This does not execute the explicit initializer for the static fields.
For e.g.: The statement static int i = 100; is a two step process. The first step would be static int i = 0; and the second step would be i = 100; In the preparation phase the first step is executed. If we have any static initializer blocks, even these are not executed in the preparation phase. These static blocks will be executed in the initialization phase.
Each class and interface has its own run time constant pool which contains the symbolic references of the other types (classes or interfaces or array types) which are required or this class to be usable.
Resolution is the process of resolving these symbolic references and determining their concrete values. If an error occurs in this process, then a IncompatibleClassChangeError must be thrown at the same point where the symbolic reference is being used.
There are various things to be resolved as per the specification. For e.g.: Class and Interface Resolution, Field Resolution, Method Resolution, Interface Method Resolution, Method Type and Method Handle Resolution and Call Site Specifier Resolution.
It is alright if you do not know enough about these resolutions. We may discuss it in some other post dedicated solely for resolution.
This phase consists of executing the initialization method of the class or interface. Now this is a tricky part, we all know that JVM supports multi threading and there can be possibilities that the same class initialization is triggered from various threads. May be due to recursive initialization requests. The JVM has full responsibility of synchronizing this initialization activity.
An initialization lock is acquired by a thread before initiating the Initialization. Once the lock is acquired the initialization starts.
Exiting the Java Virtual Machine
The Java Virtual Machine exits when some thread invokes the exit method of class Runtime or class System, or the halt method of class Runtime, and the exit or halt operation is permitted by the security manager.
This is a brief explanation of the phases which are executed after the JVM starts. We will dwell deep into the class loading and related issues and the resolution of symbolic references in some detailed post.
I have tried to put these steps in a very simplistic manner, if you want more details about these look out for my next post.
Keep reading and stay subscribed 🙂