Java Memory Management in a nutshell
- 1. What do Strong, Weak, Soft, and Phantom references mean, and when should they be used?
- 2. What do escaping references defensive copying and immutability mean? What is the difference between deep and shallow copies?
- 3. What are the parts of the memory model, and what are they used for? GC root
Resources:
Topics:
- StrongReference & WeakReference
- Immutable objects, defensive copying, deep copy & shallow copy
- heap, stack, stack frame
Introduction.
To achieve high performance:
- good practices in coding
- configuring JVM
Long-term storage (e.g. HDD or SSD). Main memory:
- Read-Only Memory (ROM) - non-volatile, contains e.g. starting instructions.
- Random Access Memory (RAM) - volatile, short-term memory, available for CPU to run programs.
JVM understands (interprets) bytecode - not e.g. Java or Kotlin, and translates it to the machine it’s running on.
JVM manages the Java memory (allocates, deallocates). Provides automatic memory management.
To run applications on JVM, we need 3 components:
- class loader - loading and verifying bytecode (a part of the execution engine)
- runtime data areas - storing class data, allocating memory, executing instructions (a part of the execution engine)
- Java Native Interface (JNI) - to interacts with the native libraries (written in e.g. C)
Runtime data area.
Heap
Dynamic allocation memory area. Stores runtime data, e.g. objects. Reallocation process is managed by Garbage Collector.
Stack
Stores primitive values and pointers. One per a thread. Every method call create a new frame on top of the stack. The current frames on all stacks can bew executed in the same time, which is the concept of concurrency.
Metaspace
Metadata, shared by all threads. Contains runtime code, static variables, constant pools and constructor code.
Program counter register
One per a thread - tracks the sequence of statements and which one is currently being executed.
Native method stack
One per a thread. Similar to JVM stack, stores values for the native code.
Creating and storing variables on the stack.
Data types are checked only by the compiler, JVM doesn’t do it on runtime. There are 2 types of variables: primitives and reference types. Primitives and references are stored on the stack, but the objects assigned to references are stored on the heap.
There 8 types of primitive values (int, byte, short, long float, double boolean, char).
References types: class references, array references, interface references, null.
Stack and frames
The stack is the memory that is used for executing methods. The stack consists of frames, which are being created every invocation of a method. When the method execution is finished, the frame is removed.
StackOverFlowError - stack memory is too small to store what is needed for the frame.
OutOfMemoryError - not enough space for a new stack for a new thread.
Frame elements
Array of local variables - this array length is set during compile time. Single spots for: int, short, char, float, byte, boolean, and reference. Double spots for: long and double (they are 64 bits in size).
In instance methods, the first element is a reference to the this
object. For static methods parameters given to the
method start from the first element.
Operand stack - it’s a stack for operations like e.g. preparing parameters to be sent to other methods or results from other methods.
Frame data - contains of constant pool reference, and e.g. additional information for JVM to link the class to other classes during runtime.
Heap
References to objects are stored on the stack, but objects are stored on the heap. The heap memory holds all the objects that exists in the application, and the objects can be accessed from everywhere in the application using the object reference.
Metaspace
Metadata necessary for runtime.