Java Memory Model
Java Memory Model
JVM is a model of whole computer so this model naturally includes a memory model - Java Memory Model(a.k.a JMM).
Computer Memory
Computer with 2 or more cpus is able to run one thread per cpu concurrently.
CPU can access register much faster than cpu cache memory, and cpu cache memory faster than RAM.
All cpus can access the main memory.
Typically, when a CPU needs to access main memory it will read part of main memory into its CPU cache. It may even read part of the cache into its internal registers and then perform operations on it. When the CPU needs to write the result back to main memory it will flush the value from its internal register to the cache memory, and at some point flush the value back to main memory.
The values stored in the cache memory is typically flushed back to main memory when the CPU needs to store something else in the cache memory. The CPU cache can have data written to part of its memory at a time, and flush part of its memory at a time. It does not have to read / write the full cache each time it is updated. Typically the cache is updated in smaller memory blocks called “cache lines”. One or more cache lines may be read into the cache memory, and one or mor cache lines may be flushed back to main memory again.
JMM
JMM consist of heap and thread stack.
JMM Thread Stack
- Each thread has its own thread stack in JMM
- The thread stack contains the methods which the thread call and its local variables.
- A thread can only access its own thread stack
- Local variables created by a thread are invisible for other threads, even if two threads are executing the exact same code, the two treads will still create the local variables of that code in their own thread stack.
Local variables
A local variable may be
- primitive type, e.g. int, these variables are stored on the thread stack.
- a reference to an object. In that case the reference(the local variable) is kept on the thread stack, but the object itself is stored on the heap
One thread may pass a copy of a primitive variable to another thread, but it cannot share the primitive local variable itself.
JMM Heap
The heap stores following information:
-
The objects, regardless of what thread create the object.
This includes the objects of primitive types(e.g. Integer, Long). It does not matter if an object was created and assigned to a local variable, or created as a member variable of another object.
- Object’s member variables, no matter it is a primitive type or it is a reference to an object.
- Static class variables
Objects on the heap can be accessed by all threads that have a reference to this object.
Example
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 {
//static variable pointing to instance of MySharedObject
public static final MySharedObject sharedInstance =
new MySharedObject();
//member variables pointing to two objects on the heap
public Integer object2 = new Integer(22);
public Integer object4 = new Integer(44);
public long member1 = 12345;
public long member2 = 67890;
}
Each thread executing methodOne()
will create its own copy of localVariable1
and localVariable1
on their respective thread stacks.
localVariable1
only live on each thread’s thread stack. One thread cannot see changes another thread makes to its copy of
localVariable1
.
Each thread executing methodOne()
will also create their own copy of localVariable2
.
However, the two different copies of localVariable2
both pointing to the same object on the heap because they are object referenced
by a static variable. There is only one MySharedObject
on the heap.
The two Integer objects of MySharedObject
are stroed on heap as well as the two primitive variables member1
and member2
.
methodTwo()
create a local variable named localVariable1
which is an object reference to an Integer
object.
The localVariable1
reference will be stored in own thread stack.
While the two Integer
(if there are two threads executing methodTwo(), then there are two Integer instance) objects
will be stored on the heap.
How JMM works with Main Memory
When objects and variables can be stored in various different memory areas, this can result in two main problems:
-
The update of shared objects may not be visible to other threads as long as the cpu cache has not been flushed back to main memory
To solve this problem, java
volatile
keyword can be used. It guarantee that the given variable is read directly from main memory and always written back to main memory when updated. -
Race conditions when reading, checking and writing shared variables.
Java
synchronized
can be used to solve this problem. It make sure that only one thread can enter a given code at any time. It also guarantee that all variables accessed inside the synchronized will be read from main memory and when the thread exits synchronized code, all updated variables will be flushed back to main memory, regardless of whether the variable is declaredvolatile
or not.
Note : this post is from Jakob Jenkov