Core Java

JVM Architecture: JVM Class loader and Runtime Data Areas

Hello readers! In the previous article of JVM series, developers learned about the Java Virtual Machine (JVM) and it’s architecture. This tutorial will help developers to correctly answer the questions on below topics:

  • ClassLoader Subsystem
  • Runtime Data Areas

1. Introduction

Before moving further let’s take a look at the Java Virtual Machine and its basic characteristics.

1.1 What is Java Virtual Machine (JVM)?

Java Virtual Machine (JVM) is an abstract virtual machine that resides on your computer and provides a runtime environment for the Java bytecode to get executed. JVM is available for many hardware and software platforms but few Java developers know that the Java Runtime Environment (JRE) is the implementation of the Java Virtual Machine (JVM). JVM analyze the bytecode, interprets it, and execute the same bytecode to display the output.

The basic function of JVM is to execute the compiled .class files (i.e. the bytecode) and generate an output. Do note, each operating system has a different JVM, but the generated bytecode output is the same across all operating systems. This means that the bytecode generated on Windows OS can also run on Linux OS and vice-versa, thus making Java as a platform independent language.

Fig. 1: An overview of Java Virtual Machine
Fig. 1: An overview of Java Virtual Machine

1.1.1 What JVM does?

Java Virtual machine performs the following operations:

  • Loading of the required .class and jar files
  • Assigning references and verification of the code
  • Execution of the code
  • Provides a runtime environment for the Java bytecode

1.1.2 JVM Internal Architecture

The following diagram shows the key internal components of Java Virtual Machine that conforms to the JVM specification.

Fig. 2: Java Virtual Machine architecture
Fig. 2: Java Virtual Machine architecture

The classloader and runtime data areas components that are shown in Fig. 2 are each explained below.

1.2 ClassLoader Subsystem

The classloader subsystem is an essential core of the Java Virtual machine and is used for loading/reading the .class files and saving the bytecode in the JVM method area. This subsystem handles the dynamic class loading functionality and performs three major functions i.e.:

  • Loading: This component handles the loading of the .class files from the hardware system into the JVM memory and stores the binary data (such as fully qualified class-name, immediate parent class-name, information about methods, variables, constructors etc.) in the method areas. For every loaded .class file, JVM immediately creates an object on the heap memory of type java.lang.class. Do remember, even though the developers call a class multiple time, only one class object will be created. There are three main types of classloaders:
    • Bootstrap or Primordial ClassLoader: This classloader is responsible for loading the internal core java classes present in the rt.jar and other classes present in the java.lang.* package. It is by-default available with every JVM and is written in native C/C++ languages. This classloader has no parents and if developers call the String.class.getClassLoader(), it will return null and any code based on that will throw the NullPointerException in Java
    • Extension ClassLoader: This classloader is the child class of Primordial classloader and is responsible for loading the classes from the extension classpath (i.e. jdk\jre\lib\ext). It is written in Java language and the corresponding .class file is sun.misc.Launcher$ExtClassLoder.class
    • Application or System ClassLoader: This classloader is the child class of Extension classloader and is responsible for loading the classes from the system classpath. It internally uses the ‘CLASSPATH‘ environment variable and is written in Java language. System classloader in JVM is implemented by sun.misc.Launcher$AppClassLoader.class
  • Linking: This component performs the linking of a class or an interface. As this component involves the allocation of new data structures, it may throw the OutOfMemoryError and performs the three important activities:
    • Verification: It is a process of checking the binary representation of a class and validating whether the generated .class file is valid or not. This process is performed by the Bytecode verifier and if the generated .class file is not valid, a VerifyError is thrown
    • Preparation: It is a process of assigning the memory for the class level or interface level static variables and assigns the default values
    • Resolution: It is a process of changing the symbolic references with the original memory references from the method area
  • Initialization: This component performs the final phase of the class loading where all the static variables are assigned the original values and the static blocks are executed from the parent to the child class. This process requires careful synchronization as JVM is multithreaded and some threads may try to initialize the same class or interface at the same time.

Fig. 3: An overview of ClassLoader Subsystem
Fig. 3: An overview of ClassLoader Subsystem

1.2.1 How ClassLoader works in Java?

Classloader in Java works in three principles i.e. Delegation, Visibility, and Uniqueness.

Fig. 4: Classloading mechanism in Java
Fig. 4: Classloading mechanism in Java

  • Delegation: According to this:
    • Whenever the virtual machine come across a class, the JVM will check whether the specified .class file is loaded or not
    • If the .class file is already loaded in the method area, then JVM will consider that class. If not, JVM requests the classloader subsystem to load that specific class
    • The classloader system passes the request to the Application classloader which in turn delegates this request to the Extension classloader. The Extension classloader will again delegate this request to the Primordial classloader
    • The primordial classloader will search this class in the bootstrap classpath (i.e. jdk\jre\lib\rt.jar). If found, the corresponding .class file is loaded
    • If not, the primordial classloader delegates the request to the extension classloader. This will search the class in the jdk\jre\lib\ext path. If found, the corresponding .class file is loaded
    • If not, the extension classloader delegates the request to the application classloader and will search the class in the application’s classpath. If found, it is loaded otherwise developers will get a ClassNotFoundException at runtime
  • Visibility: According to this:
    • Application classloader has a visibility to see the classes loaded by the parent classloaders but vice-versa is not true i.e. If a class is loaded by the system classloader and later again trying to explicitly load the same class using the extension classloader will throw a ClassNotFoundException at runtime. For instance:
      // Printing the classloader of this class.
      System.out.println("Test.getClass().getClassLoader()?= " + Test.class.getClassLoader());
      			
      // Trying to explicitly load the class again using the extension classloader.
      Class.forName("com.jcg.classloading.test.Test", true,  Test.class.getClassLoader().getParent());
      
  • Uniqueness: According to this:
    • Class loaded by the parent classloader should not be again loaded by the child classloader

1.2.2 How classes are loaded in Java?

Classloaders are hierarchical. The very first class in an application is specifically loaded with the help of static main() method. All the subsequent classes are either loaded by the Static or the Dynamic class loading techniques.

  • Static class loading: In this technique classes are statically loaded via the new operator
  • Dynamic class loading: In this technique classes are programmatically loaded by using the Class.forName() or the loadClass() method. The difference between the two is that the former one initializes the object after loading it while the latter one only loads the class but doesn’t initialize the object

1.3 Runtime Data Areas

As shown in Fig. 5, this subsystem is divided into five major components i.e.

Fig. 5: JVM Runtime Data Areas
Fig. 5: JVM Runtime Data Areas

  • Method Area: This component holds the class level data of each .class file such as metadata, constant runtime pool, static variables, the code for the methods etc. There is only one method area per JVM and is shared among all the classes. The memory allocated to this area is by-default allocated by the JVM or can be increased as per the computation needs. The following exceptional condition is associated with this area i.e.
    • If the method area is not available to satisfy a memory allocation request, the JVM throws an OutOfMemory error
  • Heap Area: This component is a part of JVM memory where all the objects and its corresponding instance variables and arrays are stored. This memory area is created on the JVM start-up and there is only one heap area shared across multiple threads as the data stored in this area is not thread-safe. If the objects stored in the heap memory have no reference, memory for that object is reclaimed by the garbage collector (i.e. the automatic storage management system); objects in this area are never explicitly deallocated. The following exceptional condition is associated with this area i.e.
    • If the computation requires more heap area than the available, JVM throws an OutOfMemory error
  • Stack Area: This component is again a part of JVM memory where all the temporary variables are stored. This area has stack frames and allocates one frame for each thread. Once the thread execution is completed, this frame also gets destroyed. The stack area is thread-safe as it is not a shared resource and is divided into three sub-entities such as:
    • Local variable array: These local variables are used by the virtual machine to pass the parameters upon the method invocation
    • Operand Stack: This place is the actual place to perform operations during a method execution. The virtual machine takes the operand from the stack, operates on them, and pushes the result back onto the operand stack. This stack also prepares the arguments to be passed to a method and even to receive the method results
    • Frame data

    The following exceptional condition is associated with this area i.e.

    • If the thread processing requires the virtual machine stack than its permissible limit, the JVM throws a StackOverflow error
    • If the virtual machine stack is insufficiently expanded, the JVM throws a OutOfMemory error
  • PC (Program Counter) Registers: This component holds the address of the JVM instruction which is currently executing. Each thread in Java has its own PC register to hold the address of the currently executing instruction
  • Native Method Stacks: This component is written in a different language and holds the native method information. Every thread in Java has a separate native method stack. The following exceptional condition is associated with this area i.e.
    • If the thread processing requires the native stack than its permissible limit, the JVM throws a StackOverflow error
    • If the native method stack is insufficiently expanded, the JVM throws a OutOfMemory error

That’s all for this post. Happy Learning!

2. Conclusion

In this tutorial, developers had an overview of the virtual machine ClassLoader and Runtime Data Areas components. You can download the sample code in the Downloads section.

3. Download the Source Code

This was a tutorial of virtual machine ClassLoader and Runtime Data Areas components.

Download
You can download the source code of this tutorial here: JVM_Example

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button