Without automatic memory management, software can face following two problems:
1) Dangling pointer i.e. referencing a freed memory location.
Ideally such a memory location should have never been freed if it had a reference.
2) Memory leakage - This can happen if programmer mistakenly loses all references to an allocated memory.
This kind of memory can then never be accessed from the code and so can never be freed.
This causes memory leakage.
A garbage collector should achieve the following:
1) Not free a valid memory.
2) Should free the non-referencable memory as soon as possible.
3) Should avoid fragmentation of memory.
4) Should be thread safe. Freeing or allocating memory should not corrupt any blocked or running thread's memory space.
Java Memory Manager
The Java GC divides memory into three generations: Young, Old and Permanent.
The young generation is further divided into three parts: Eden, From and To. Eden is the place where all objects are first created.
When the Eden is full or sufficient time has elapsed, the GC makes a pass through the Eden, collects
the surviving objects in the Eden and puts them in the For space.
In a typical program, most of the objects are short lived. Thus, in this pass itself, more than 90%
objects are found dead in Eden. The surviving objects are put into the From space and whole of Eden is cleared
again for new objects. This kind of garbage collection happens frequently to make sure that short-lived
objects do not continue to claim the memory after they are dead.
Another advantage of this approach is that since surviving objects are copied over to another space,
the problem of fragmentation is solved to a large extent.
The To space is not present in all the JVM implementations.
It is used to receive the survivors of Eden and From spaces when both of them are cleaned together.
This approach prevents fragmentation in a manner similar to objects' movement from Eden to From space
Eden ---> From space (Prevents fragmentation in Eden space)
Eden + From ---> To space (Prevents fragmentation in Eden and From spaces)
Objects that move from Young to Old generation typically do not get freed in several collections.
Hence, the garbage collection for them is much more costly as compared to the young generation ones.
Due to this, the GC for this generation runs much less frequently.
The permanent generation holds objects that are not expected to die at all like the singleton objects,
objects describing classes and methods, as well as the classes and methods themselves etc.
Memory management for multithreaded applications
For multi-threaded applications, JVM uses Thread-Local Allocation Buffers (TLABs).
In this approach, each thread has its own small portion of the
generation from which memory is allocated.
Since only one thread can be allocating into each TLAB, allocation can take
place much faster without any locks or synchronization.
When a thread fills up its TLAB, only then synchronization constructs are used but this does not happen very frequently.
Using JVisualVM to debug Java memory problems
JVisualVM is a powerful tool shipped with JDK and is present in jdk/bin/ folder.
Below is a screenshot obtained through the "Visual GC" plugin.
The Visual GC plugin can be selected from Tools > Plugins > Available Plugins
If its not present there, it can be downloaded by clicking the Reload Catalog button.
The plugin can also be downloaded manually from visualvm site and
installed using the Tools > Plugins > Downloaded > Add Plugins button.
Another cool feature in JVisualVM is its ability to show thread related information as shown below:
Parallel Garbage Collector
As the number of cores per machine are increasing rapidly, it is obvious to expect a GC scheme that is itself multi-threaded. For younger generation, the number of root objects are divided per thread and the "To" area is also divided among
the threads. Each thread works on the objects allocated to it and puts them to its designated area in the "To" area.
Thus, the young generation parallel algorithm simply divides the serial collection algorithm equally among multiple threads.
This increases efficiency although more fragmentation is expected than the single threaded collection.
For older generation, the memory space is divided equally among GC threads.
But since most of the older generation objects are expected to live longer, the threads do not proceed with garbage collection
Every thread first calculates the ratio of live to dead objects in its space.
Dense areas (which have more live objects than dead ones) are left untouched while
less dense areas are selected for garbage collection.
When the less dense areas have been garbage collected, they are compacted to move
all the live objects to one location and freeing a large area of contiguous memory.
Again, this kind of parallel garbage collection works faster but leaves some holes (in the dense areas).
Concurrent Mark-Sweep (CMS) Collector
The parallel collectors described above need to stop all the user threads in between to successfully complete their work.
There are applications for which this may not be desirable.
For such applications, the JVM provides another GC scheme called the Concurrent Mark-Sweep Collector.
This collector works in four steps:
Step 1: Each GC thread gets a portion of the directly accessible objects for analysis.
Step 2: From the directly accessible objects, the threads figure out other objects accessible from them.
Step 3: Since, object references might have changed while above two steps were being run, a third
step is required where only the changed objects are analyzed.
Step 4: At this stage, all the objects have been analyzed and each thread can continue to free the objects it found dead.
Note that there is no compaction of free space in this strategy as compaction requires moving of live objects and
that movement requires all the user threads to stop.
Since compaction is not there, the amount of fragmentation is huge in this approach.
To fix this fragmentation, JVM uses free lists to keep track of the free spaces available.
When a request for a new object comes, the free lists are examined to get a best match for the amount of requested memory.
The CMS collector also keeps track of the popular memory size requests and optimizes the popular requests by
keeping popular free sizes readily available.
Difference between Parallel and Concurrent GC is that parallel GC has multiple threads to do garbage collection but it still
stops all the other threads when it begins garbage collection.
Concurrent GC is also multi-threaded and it also stops other threads but threads of concurrent GC run in parallel with the
application threads. They run in parallel and pause the application threads only when there is a real need of doing so.
Due to this, concurrent GC is recommended only when there are lot of CPUs and GC threads do not burden
the application more than required.
But due to concurrent nature of the GC, garbage collection becomes more efficient and more regularly cleaned.
With CMS, young and old generations do not grow as much as they grow in parallel GC.
The G1 collector is fully supported in Oracle JDK 7 update 4 and future releases.
It is a server-side collector whose basic operation is same as the CMS collector but in addition to the CMS collection scheme,
it adds pause prediction model to meet a user-defined pause time target.
The number of regions to compact are based on the specified pause time target.
The G1 collector does not guarantee to take exactly the user-define pause time but it does so on a best-effort basis by
analyzing data from previous collections and trying to estimate the number of regions which can be collected within the specified target time.
Another important difference between the CMS and G1 collectors is that G1 is a compacting collector and leaves no fragmentation.
G1 collector is recommended for use in applications where the object allocation rates vary widely.
In such cases, the garbage collector can spend very less time during times of high load and make up for this shallow garbge
collection during times of low load (low object creation times).
Switches for customizing garbage collection behavior
Sets the initial heap size for when the JVM starts.
Sets the maximum heap size.
Sets the size of the Young Generation.
Sets the starting size of the Permanent Generation.
Sets the maximum size of the Permanent Generation
Enables the serial garbage collector.
Enables the parallel garbage collector
Helps specify the no of parallel garbage collector threads.
Enables the CMS garbage collector
Helps specify the no of parallel garbage collector threads for the CMS collector.
Enables the G1 garbage collector
Sets a target for the maximum GC pause time.
Switch to initiate collection when a specific percentage of heap has been exhausted.
Got a thought to share or found a bug in the code? We'd love to hear from you: