Memory in the JVM

Available

Understanding Memory in the JVM

As you already know, the JVM runs Java programs within itself. Like any virtual machine, it has its own memory organization system.

The internal memory layout indicates how your Java application works. In this way, bottlenecks in the operation of applications and algorithms can be identified. Let's see how it works.

Understanding Memory in the JVM

Important! The original Java model was not good enough, so it was revised in Java 1.5. This version is used to this day (Java 14+).

Thread stack

The Java memory model used internally by the JVM divides memory into thread stacks and heaps. Let's look at the Java memory model, logically divided into blocks:

Thread stack

All threads running in the JVM have their own stack . The stack, in turn, holds information about which methods the thread has called. I will call this the “call stack”. The call stack resumes as soon as the thread executes its code.

The thread's stack contains all the local variables required to execute methods on the thread's stack. A thread can only access its own stack. Local variables are not visible to other threads, only to the thread that created them. In a situation where two threads are executing the same code, they both create their own local variables. Thus, each thread has its own version of each local variable.

All local variables of primitive types ( boolean , byte , short , char , int , long , float , double ) are stored entirely on the thread stack and are not visible to other threads. One thread can pass a copy of a primitive variable to another thread, but cannot share a primitive local variable.

Heap

The heap contains all the objects created in your application, regardless of which thread created the object. This includes wrappers of primitive types (for example, Byte , Integer , Long , and so on). It doesn't matter if the object was created and assigned to a local variable or created as a member variable of another object, it is stored on the heap.

Below is a diagram that illustrates the call stack and local variables (they are stored on stacks) as well as objects (they are stored on the heap):

Heap

In the case where the local variable is of a primitive type, it is stored on the thread's stack.

A local variable can also be a reference to an object. In this case, the reference (local variable) is stored on the thread stack, but the object itself is stored on the heap.

An object contains methods, these methods contain local variables. These local variables are also stored on the thread stack, even if the object that owns the method is stored on the heap.

An object's member variables are stored on the heap along with the object itself. This is true both when the member variable is of a primitive type and when it is an object reference.

Static class variables are also stored on the heap along with the class definition.

Interaction with objects

Objects on the heap can be accessed by all threads that have a reference to the object. If a thread has access to an object, then it can access the object's variables. If two threads call a method on the same object at the same time, they will both have access to the object's member variables, but each thread will have its own copy of the local variables.

Interaction with objects (heap)

Two threads have a set of local variables.Local Variable 2points to a shared object on the heap (Object 3). Each of the threads has its own copy of the local variable with its own reference. Their references are local variables and are therefore stored on thread stacks. However, two different references point to the same object on the heap.

Please note that the generalObject 3has links toObject 2AndObject 4as member variables (shown by arrows). Through these links, two threads can accessObject 2AndObject4.

The diagram also shows a local variable (local variable 1from methodTwo ). Each copy of it contains different references that point to two different objects (Object 1AndObject 5) and not the same one. Theoretically, both threads can access bothObject 1, so toObject 5if they have references to both of these objects. But in the diagram above, each thread only has a reference to one of the two objects.

An example of interaction with objects

Let's see how we can demonstrate the work in code:

public class MySomeRunnable implements Runnable() {

    public void run() {
        one();
    }

    public void one() {
        int localOne = 1;

        Shared localTwo = Shared.instance;

        //... do something with local variables

        two();
    }

    public void two() {
        Integer localOne = 2;

        //... do something with local variables
    }
}
public class Shared {

    // store an instance of our object in a variable

    public static final Shared instance = new Shared();

    // member variables pointing to two objects on the heap

    public Integer object2 = new Integer(22);
    public Integer object4 = new Integer(44);
}

The run() method calls the one() method , and one() in turn calls two() .

The one() method declares a primitive local variable (localOne) of type int and a local variable (localTwo), which is a reference to an object.

Each thread executing the one() method will create its own copylocalOneAndlocalTwoin your stack. VariableslocalOnewill be completely separated from each other, being on the stack of each thread. One thread cannot see what changes another thread makes to its copylocalOne.

Each thread executing the one() method also creates its own copylocalTwo. However, two different copieslocalTwoend up pointing to the same object on the heap. The fact is thatlocalTwopoints to the object referenced by the static variableinstance. There is only one copy of a static variable, and that copy is stored on the heap.

So both copieslocalTwoend up pointing to the same Shared instance . The Shared instance is also stored on the heap. It matchesObject 3in the diagram above.

Note that the Shared class also contains two member variables. The member variables themselves are stored on the heap along with the object. Two member variables point to two other objectsInteger. These integer objects correspond toObject 2AndObject 4on the diagram.

Also note that the two() method creates a local variable namedlocalOne. This local variable is a reference to an object of type Integer . The method sets the linklocalOneto point to a new Integer instance . The link will be stored in its copylocalOnefor each thread. Two Integer instances will be stored on the heap, and because the method creates a new Integer object each time it is executed, the two threads executing this method will create separate Integer instances . They matchObject 1AndObject 5in the diagram above.

Notice also the two member variables in the Shared class of type Integer , which is a primitive type. Because these variables are member variables, they are still stored on the heap along with the object. Only local variables are stored on the thread stack.

Comments
  • Popular
  • New
  • Old
You must be signed in to leave a comment
This page doesn't have any comments yet