As I had to study their operation, I decided to post about the basic concepts of memory in Java.
Note: This post is a bit old so the default strategy on JVM may have changed since. Although the basics are the same.
Heap and Non-Heap
To begin with, Java divides memory into two distinct segments:
- Heap: User objects, variables, …
- Non-Heap/Perm: code, metadata, …
The one that interests us is the Heap, as it is what we can “control”. You can configure the Non-Heap Memory (Perm) size with the parameter
MaxPermSize. But this is only useful if the application is loaded or dynamically generate many different classes.
The heap is divided into two generations according to their lifetime:
- Young Generation
- Old Generation
Usually the younger generation is composed of local variables and temporary objects. While the older generation usually consists of structures that are necessary during the execution: configurations, viewports,…
Also, the younger generation is divided into two:
- Eden: This is where objects are created initially
- Survivor: It’s like the limbo through which we pass from the young to the old generation. It is usually composed into two parts.
You can specify the ratio between the space of the young generation and old generation with the virtual machine parameter
-XX: NewRatio .
The Garbage Collector
The Garbage Collector (
Garbage Collector ) performs two types of periodic tasks:
- Minor Collection: Reviews quickly the younger generation.
- Major Collection: Reviews all the memory, mainly the older generation.
The garbage collector runs at the same time as the normal program execution. Each execution involves a small pause (usually milliseconds) in all the threads that are running at that time. As memory remains more or less freed, the Garbage Collector will be limited to minor collections, to not interfere with the flow of the application.
To get a better idea of how it works, you can add
-verbose:gc to the virtual machine. This command will print each time you run the garbage collector and the area of memory that has been successfully unleashed.
Different memory strategies
There are several implementations of the garbage collector, being the most common the
Serial Collector. This implementation, as well as being the simplest, uses only one processor. If you’re using a more powerful machine with multiple processors and a good amount of physical memory, you can activate the
Parallel Collector , which uses multiple CPUs at the same time. This improves the way in which the garbage collector works, but it can also parallelize the flow of normal execution of the application.
For proper operation and cleaning of memory, we should have little short-lived temporary objects better than long, durable objects. The small temporary objects will stay in the Eden, so they will be collected much earlier and much faster.
Also, having unused objects in memory, although they do not disrupt the execution of the program, will slow the execution of the garbage collection. Because you have to process them over and over again to check if they can be deleted.
Forcing a collect of memory garbage
At some point it may seem tempting to force a Garbage Collector implementation calling
System.gc(). However, this will force a major collection asynchronously, breaking up all the heuristics of the Garbage Collector. It is so discouraged that there is an option in the virtual machine to disable these calls:
-XX: + DisableExplicitGC .
To help the task of garbage collection, there are three types of references :
- Weak : It does not prevent the garbage collector take it.
- Soft : As the weak, but the garbage collector a little more respect him and takes him only if you need memory. Useful for caching, but can be misleading.
- Phantom: Always returns null, not really accesses the object, but can serve serve to clear before they take the object that binds it.
A class that explains quite well the operation of such references is
WeakHashMap. It works as a HashMap, but using Weak References. Thus, if the key contains an object which is only referenced in the HashMap, it is no longer considered useful and is cleared.