Java Static Keyword and the JVM Architecture

·

7 min read

Introduction

In instances where members are required to be accessed by the class name rather than the object references, the static keyword can be used to declare the members. It would, therefore, mean that such members will be shared by all the instances of a class without making copies per instance. The static keyword is used in purposes for memory management.

Static can be used for the following members:

  • Variable
  • Method
  • Block

Program illustrating static keyword used with different members

package demo;

public class StaticClass {

    static int a;
    static int b;
    static {
        a=10;
        b=100;
        System.out.println("This is a Static block");
    }
    static void display() {
        System.out.println("Static Variables:: a="+a+" b="+b );
    }
    public static void main(String[] args) {
        System.out.println("This is the main method");
        StaticClass.display();
    }
}

OUTPUT: image.png

A class could contain Static and Non-Static members, constructors and other instance variables. During the program compilation phase, the java virtual machine(JVM) follows a certain order of execution for all these different types of members. Let us understand the same with the help of the JVM Architecture.

Java Virtual Machine Architecture

A program in java is compiled by the Java compiler to produce a .class file which basically contains the byte code. The byte code is what makes java a platform independent language. When this java program needs to be executed on a client machine, the .class file is run on a abstract virtual machine which would analyze and interpret the byte code to enable us to view the output. This component is called the JAVA VIRTUAL MACHINE(JVM).

image.png Let us discuss each of the component and the static context during the class execution below.

CLASS LOADER SUBSYTEMS

An important area in the JVM, called the CLASS LOADER SUBSYTEM, loads the .class files and saves the files to method area.

image.png

Three main operation performed by this subsystem:

  1. Loading: Primarily handles the loading of the required classes with the help of 3 built-in class loaders, i.e:

    • Bootstrap ClassLoader - responsible for loading built-in java class files contained in
      packages of java.lang, java.util, java.io, etc.
    • Extension ClassLoader - loads all the jar files specified in the libraries folder in $JAVA_HOME PATH.
    • Application ClassLoader- responsible for loading all class files written by programmers specified in the CLASS_PATH.

    The above modules work by the delegation hierarchy algorithm which involves interaction among these modules and delegating work by exchanging requests based on scenarios.

  2. Linking: Two main phases under the linking stage , i.e:

    • Bytecode verifier which checks for the syntax and correctness of the loaded byte code.
    • Preparation stage where a check for any static variables will happen and if present, memory will be allocated on the heap memory area. This component will verify the generated bytecode and assign references.
  3. Initialization: In this phase the static blocks will be executes and the static variables will be initialized with their default values.

JVM DATA AREAS

JVM consists of the following five runtime areas

  • Method Area- created on the boot of virtual machine and is the area where class loader subsystems loads the class files as discussed before. It is a sharable memory area to all the threads.
  • Heap Area - it is main memory of JVM which is the storage space for objects and arrays.
  • Stack Area - Runtime environment of the JVM. Even static variables/methods will be brought to this are for execution. A new frame is created for each method invocation whose life span will be same as that of the method.
  • Program Counter Registers Area - created for each thread. Responsible for storing the next instruction address to be executed.
  • Native Methods Stacks area - executed native methods that includes code written in other languages such as c, c++ or any assembly languages.

image.png

EXECUTION ENGINE

The engine consists of 3 main components:

  • Interpreter: It reads the byte code and interprets into the machine code. The interpreter runs interprets the code for each method call even though for multiple calls to the same method causing degradation in performance.
  • Just-In-Time(JIT) Compiler: introduced to overcome the performance issue where the semantics of the code used multiple times are compiled and stores during run-time. During the method invocation, the compiled code is called rather than interpreting it again.
  • Garbage Collector: A Daemon program which automatically deallocated memory for unreferenced objects in the heap area.

image.png

Overall JVM Architecture

image.png

ORDER OF EXECUTION DURING CLASS COMPILATION

  1. STATIC BLOCK - The block enclosed with static{} is executed first which usually is used to initialize static variables.
  2. JAVA INTIALIZATION BLOCK : Next to be executed are the statements enclosed with {} which would executed for each object instantiation - the constructor calls this block internally.
  3. CONSTRUCTOR : The constructor is then called to initialize the instance variables of the class. All the other static , non-static methods will be executed in the order it is called by the programmer explicitly. Below program to illustrate the same:
package demo;
class Demo
{
    static int a;
    static int b;

    static 
    {
        System.out.println("Invoking Static block");
        a=10;
        b=20;

    }
    static void staticDisplay()
    {
        System.out.println("Invoking Static method");
        System.out.print("Static variable values: ");
        System.out.println(a +" "+b);
    }
    int x;
    int y;

    {
        x=10;
        y=20;
        System.out.println("Invoking Non-static Java block");
    }
    Demo()
    {

        System.out.println("Invoking Class Constructor");
    }
    void nonStaticDisplay()
    {
        System.out.println("Invoking Non-static method");
        System.out.print("Non-static variable values: ");
        System.out.println(x +" "+y);

    }
    void disp2()
    {
        nonStaticDisplay();
    }
}

public class StaticMembers 
{

    public static void main(String[] args) {        
        Demo d=new Demo();
        d.staticDisplay();
        d.nonStaticDisplay();
    }

}

Output: image.png

If a set of operations needs to be performed during class loading, they can be created inside the Static block. Similarly, those operations which needs to be performed for each object during their instantiation, they can be created in the Java Initialization Block.

Important points to remember for static keyword

  1. Memory is allocated during the class loading phase by the JVM.
  2. The memory for such members is provided on the HEAP area and is done only once.
  3. Only one copy of the these variables is created and is shared by all instances of the class. Hence, can be called Object-independent.
  4. These members are called using the class name.
  5. They can be used/accessed inside static as well as non-static elements of the program.
  6. Static methods can be used to call other static methods.
  7. Static methods can not be used to call non-static methods, neither can they be used to access non-static data members.

Program Illustration that static variables are shared among all class objects

package demo;

      class myClass
     {
        int a;
        int b;
        int objcounter;
        static int count;

    static
    {
        count=1;
        System.out.println("Inside Static Block! Initialized static variable 
        Count to "+count);
    }

    {                                                                                        // non-static block
        count++;
        objcounter++;
        System.out.println("Inside non-Static Block - static variable ="+count+",non-static 
        variable count ="+objcounter);
    }

    myClass()
    {
        this(100);
        System.out.println("First constructor invoked");
    }
    myClass(int a)
    {
        this.a=a;

    }
    myClass(int a, int b)
    {
        this.a=a;
        this.b=b;
    }
}
   public class staticDemo{
        public static void main(String[] args) {
        myClass d1=new myClass();
        myClass d2=new myClass(10,20);
        myClass d3=new myClass(10);

    System.out.println("Static count variable " + myClass.count);
    }
}

Explanation: When the above program is executed by the JVM, the static variable count is allocated a memory in the heap. The static block will be executed next where count will be initialized to one. The non-static block will be invoked for each of the three object calls where all objects will share a single copy of static variable 'count' but will have individual copies for non-static variable 'objcounter'. Hence, the static variable will have 4 at the end of execution as observed from the output below: image.png

Conclusion

In this article, we have discussed the detailed architecture of the java virtual machine and the significance of each of its components. We have also looked at how the java compiler executes the source programs by compiling it to byte code. We have explored the static keyword and how it is stored in the java data areas. We have discussed programs in the static context and the order of execution when a java program is executed.

References

  1. Image Source Credits - JVM ARCHITECTURE from javatutorial.net