Multithreading. The Java Memory Model (Part 1)

Hello, Habr! I present to your attention the translation of the first part of the article "Java Memory Model" by Jakob Jenkov.



I am going through training in Java and needed to study the article Java Memory Model . I translated it for better understanding, but so that the good would not be lost, I decided to share it with the community. I think it will be useful for beginners, and if someone likes it, I will translate the rest.



The original Java memory model was not very good, so it was revised in Java 1.5. This version is still in use today (Java 14+).





Internal Java memory model



The Java memory model used internally by the JVM divides memory into a thread stack and a heap. This diagram illustrates the Java memory model from a logical point of view:



image



Each thread running in the Java virtual machine has its own stack. The stack contains information about which methods the thread called to reach the current point of execution. I will refer to this as the "call stack". As soon as the thread executes its code, the call stack changes.



The thread stack contains all local variables for each method that is executed (all methods in the call stack). A thread can only access its own stack. Local variables are invisible to all other threads except the thread that created them. Even if two threads are executing the same code, they will still create local variables of that code on their own stacks. 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 completely stored 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.



The heap contains all the objects created in your Java application, regardless of which thread created the object. This includes versions of objects of primitive types (eg Byte, Integer, Long, etc.). 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.



Here is a diagram illustrating the call stack and local variables that are stored on thread stacks, as well as objects that are stored on the heap: A



image



local variable can be of primitive type, in which case it is completely stored on the thread's stack.



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



An object can contain methods, and these methods can 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.



The member variables of an object 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.



Variables of a static class are also stored on the heap along with the class definition.



Objects on the heap can be accessed by all threads that have a reference to the object. When a thread has access to an object, it can also access the member variables of that object. 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.



Here is a diagram illustrating the points above:



image



Two threads have a set of local variables. Local Variable 2 points to a shared object on the heap (Object 3). That is, each of the threads has its own copy of the local variable with its own reference. Thus, two different references point to the same object on the heap.



Note that generic Object 3 has references to Object 2 and Object 4 as member variables (shown by arrows). Through these links, two threads can access Object 2 and Object 4.



The diagram also shows the local variable (Local variable 1). Each copy of it contains different references, which point to two different objects (Object 1 and Object 5), and not to the same one. In theory, both threads can access both Object 1 and Object 5 if they have references to both of these objects. But in the diagram above, each thread only references one of the two objects.



So what kind of Java code might be the result of these illustrations? Well, as simple code as the code below:



Public class MyRunnable implements Runnable() {

    public void run() {
        methodOne();
    }

    public void methodOne() {
        int localVariable1 = 45;

        MySharedObject localVariable2 =
            MySharedObject.sharedInstance;

        //... do more with local variables.

        methodTwo();
    }

    public void methodTwo() {
        Integer localVariable1 = new Integer(99);

        //... do more with local variable.
    }
}


public class MySharedObject {

    // ,    MySharedObject

    public static final MySharedObject sharedInstance =
        new MySharedObject();


    // -,      

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

    public long member1 = 12345;
    public long member2 = 67890;
}


The run () method calls methodOne () and methodOne () calls methodTwo ().



methodOne () declares a primitive local variable (localVariable1) of type int and a local variable (localVariable2) that is an object reference.



Each thread that executes the One () method will create its own copy of localVariable1 and localVariable2 on their respective stacks. The localVariable1 variables will be completely separate from each other, being on the stack of each thread. One thread cannot see what changes another thread is making to its copy of localVariable1.



Each thread that executes the One () method also creates its own copy of localVariable2. However, two different copies of localVariable2 end up pointing to the same object on the heap. The point is that localVariable2 points to the object referenced by the sharedInstance static variable. There is only one copy of the static variable and that copy is stored on the heap. Thus, both copies of localVariable2 end up pointing to the same MySharedObject instance. The MySharedObject instance is also stored on the heap. It corresponds to Object 3 in the diagram above.



Note that the MySharedObject class also contains two member variables. The member variables themselves are stored on the heap along with the object. The two member variables point to two other Integer objects. These integer objects correspond to Object 2 and Object 4 in the diagram.



Also note that methodTwo () creates a local variable named localVariable1. This local variable is a reference to an Integer object. The method sets the reference localVariable1 to point to a new Integer instance. The link will be stored in its own copy of localVariable1 for each thread. The 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 correspond to Object 1 and Object 5 in the diagram above.



Notice also the two member variables in the MySharedObject class of type long, which is a primitive type. Since 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.



Part 2 is here.



All Articles