Java Memory Management

Java uses an automatic memory management system called a garbage collector.
Whereas in other programming languages such as C the programmer has direct access to the memory who allocates memory in his code, thereby creating a lot of scope for leaks
Why programmer should know about memory management?
- To make high performance based programs.
- If we don’t know how the memory management works, we end up making objects that aren’t eligible for the automatic garbage collection.
Java memory management divides into two major parts:
- JVM Memory Structure
- Working of the Garbage Collector
JVM Memory Structure
JVM creates many run time data areas in the heap which is used during the execution of the program
- Memory areas are destroyed when the JVM exits
- Data areas are destroyed when the Thread exits
Method Area
- It is used to allocate memory to
- class structures
- method data
- constructor field data
- interfaces or special method used in class
- It is the logical part of the heap
- It is not compulsory to be garbage collected even if the garbage collection is compulsory in the heap
Heap
- Stores the actual object
- Can be fixed or dynamic in size.
- When we use a new keyword, the JVM creates an instance for the object in a heap.
- Reference of the object created from the new keyword is stored in the stack
- There is only one heap for each and every running process
- When the size of heap becomes full, the garbage is collected
For eg:
StringBuilder str=new StringBuilder();
The object is stored in the heap and the reference of the object “str” is stored in the stack
JVM memory is divided into separate parts. At broad level, JVM Heap memory divided into two parts – Young Generation and Old Generation.
Young Generation
Young Generation is divided into three parts – Eden Memory and two Survivor Memory spaces.
- Most of the newly created objects are located in the Eden memory space.
- When Eden space is filled with objects, Minor GC is performed and all the survivor objects are moved to one of the survivor spaces.
- Minor GC also checks the survivor objects and move them to the other survivor space. So at a time, one of the survivor space is always empty.
- Objects that are survived after many cycles of GC, are moved to the Old generation memory space.
Old Generation
Old Generation memory contains the objects that are long-lived and survived after many rounds of Minor GC. Usually, garbage collection is performed in Old Generation memory when it’s full. Old Generation Garbage Collection is called Major GC and usually takes a longer time.
Garbage collection in heap area is mandatory.
Reference Type
There are four types of references:
- Strong Reference: Any object having a strong reference is not eligible for garbage collection
For eg: StringBuilder sb= new StringBuilder();
- Weak Reference: If we are not sure if the object will be required again or not we can apply garbage collection on the object by using a weak reference. It destroys the object. An object can be referenced as weak Reference in the following ways.
For eg: WeakReference<StringBuilder> reference = new WeakReference<>(new StringBuilder());
- Soft Reference: These objects are collected when the application is running low on memory. All the soft reference object are collected before JVM throws are OutOfMemoryError
For eg: SoftReference<StringBuilder> reference = new SoftReference<>(new StringBuilder());
- Phantom Reference: The object with phantom reference can be collected whenever the garbage collector wants to run. . We can create phantom reference by using the following statement:
For eg:PhantomReference<StringBuilder> reference = new PhantomReference<>(new StringBuilder())
Stack Area
- It contains references to the heap object and also holds value to that object
- Can be fixed or dynamic in size
- Stack generates when thread creates stack memory is allocated per thread
- It is used to store partial results and data
- Stack Frame
- Used to store thread Data
- Components of Frame
- Local Variable Array (LVA)
- Operand Stack (OS)
- Frame Data (FD)
- Only one frame is active at a point
Frame-Current Frame
Method: Current Method
Class:Current Class
Working of Garbage Collector
- All the garbage collection makes sure that the heap has as much free space as possible.
- The function of the garbage collector is to find and delete the objects that cannot be reached.
- JVM controls the garbage collector.
- JVM decides when to perform the garbage collection.
Note: System.gc() and Runtime.gc() are the methods which requests for Garbage collection to JVM explicitly but it doesn’t ensures garbage collection as the final decision of garbage collection is of JVM only.
When garbage collection of an object occurs?
- object is eligible for garbage collection when no live thread can access it
- The garbage collector considers that object as eligible for deletion. If a program has a reference variable that refers to an object, that reference variable available to live thread, this object is called reachable.
Can a Java application run out of memory?
Yes,if we have many live objects, garbage collection does not ensures that there is enough memory. Only available memory will be managed effectively.
Types of Garbage Collection
There are five types of garbage collection are as follows:
- Serial GC: uses the mark and sweeps approach for young and old generation
- Parallel GC: It spawns N (the number of CPU cores in the system) threads for young generation garbage collection.
- Parallel Old GC: It uses multiple threads for both generations.
- Concurrent Mark Sweep (CMS) Collector: It does the garbage collection for the old generation. You can limit the number of threads in CMS collector using XX:ParalleCMSThreads=JVM option. It is also known as Concurrent Low Pause Collector.
- G1 Garbage Collector: It introduced in Java 7. Its objective is to replace the CMS collector. It is a parallel, concurrent, and CMS collector. There is no young and old generation space. It divides the heap into several equal sized heaps. It first collects the regions with lesser live data.
Garbage Collection in latest Java versions
Since Java 9, the G1 collector has been the default GC in OpenJDK and Oracle JDK.
- G1’s overall approach to garbage collection is to slice up GC pauses according to a user-supplied time target.
- This means that if you want shorter pauses, set a lower target, and if you want less of the CPU used by your GC and more used by your application, set a larger target.
- Whereas Parallel GC was a throughput-oriented collector, G1 tries to be a jack of all trades: It offers lesser throughput but better pause times.
- However, G1 isn’t a master of pause times. As the amount of work to be done during a garbage collection cycle increases, either due to a very large heap or to rapidly allocating lots of objects, the time-slicing approach starts to hit a wall
This is the problem space that JDK 12’s Shenandoah GC attacks:
- It’s a latency specialist.
- It can consistently achieve low pause times, even on large heaps.
- It might spend a bit more CPU time performing garbage collection work than the Parallel GC, but the pause times are greatly reduced.
To use Shenandoah in your application from Java 12 onwards, enable it with the following options:
-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
In Java 11, the Z Garbage Collector was only available on Linux 64 bit platform. Starting Java 14, ZGC is now available as an experimental feature on Windows as well as MacOS.
ZGC performs all expensive work concurrently, without stopping the execution of application threads for more than 10ms, which makes is suitable for applications which require low latency and/or use a very large heap (multi-terabytes).
The Z Garbage Collector is available as an experimental feature, and is enabled with the command-line options.
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
Setting the Heap Size
The most important tuning option for ZGC is setting the max heap size (-Xmx). Since ZGC is a concurrent collector a max heap size must be selected such that,
1) the heap can accommodate the live-set of your application
2) there is enough headroom in the heap to allow allocations to be serviced while the GC is running
Setting Number of Concurrent GC Threads
The second tuning option one might want to look at is setting the number of concurrent GC threads (-XX:ConcGCThreads). ZGC has heuristics to automatically select this number. This heuristic usually works well but depending on the characteristics of the application this might need to be adjusted.