Java has been considered a mature and robust programming language and one of the reasons is its excellent features for memory management. There are several options for memory management in Java that ensure the stable running of applications. This will be a complete guide to understand memory management in Java. We will be covering some important topics like how the heap works, reference types, and garbage collection.
Why should you know about memory management in Java?
You might be wondering, why would a programmer need to know about the working of memory? Java, like any other modern high-level language, has automatic memory management and an efficient garbage collector that cleans up all the unused objects and manages the memory implicitly.
It is correct that a Java developer does not need to perform any memory-related tasks but by not knowing how the garbage collector works and how Java memory is managed, you could end up collecting objects that are not eligible for garbage collecting, even if you no longer need them.
Knowing how to manage memory in Java allows you to write high-performing and optimized codes that will not crash with an OutOfMemoryError and if you ever encounter a memory-related error, you will be able to identify the problems.
Types of memory in Java
Memory is divided into two big chunks in Java: one is the stack and the other is the heap.
1. The stack
Stack memory is mainly responsible for containing all the references to objects present in heap memory and for storing actual values of primitive data types.
Variables in the stack memory have certain visibility, known as scope. Objects with the active scope are the only ones used by the methods. For instance, if you do not have any global scope variables and just the local variables in your method when it will be executed by the compiler, it can only access the objects from the stack that are present within the method’s body. It cannot access other local variables, as those are out of scope. Once the method is executed and it returns the result, the active scope will now change as the top of the stack pops out.
It is to be noted that the stack memory in Java is allocated for every individual thread. It means that whenever a thread is created and started, it gets a new share of stack memory.
2. The heap
Heap is where the actual objects are stored in the memory. As the name suggests, it’s just like a heap and does not have a certain structure like the stack that’s why the objects are then referred to by the variables present in the organized stack memory. For example, see the following line of code:
int arrayResult = new int;
The “new” keyword ensures that there is enough space available in the heap, then it creates an object of the array of integers in memory and refers to it via the “arrayResult” reference, which is then stored in the stack. Unlike the stack memory, there is only one heap memory for every JVM process. Therefore, it is a shared part of memory regardless of how many threads are running at a time.
Types of References
When it comes to references, there are different types of references in the Java programming language. Let’s take a look at each of them.
1. Strong reference
It is the most commonly used reference type and is the default reference to objects in the heap. The example above holds a strong reference to the object from the heap. The strong reference indicates that the object will not be eligible for garbage collection while there is a strong reference pointing to it.
2. Weak referencing
On the contrary to Strong referencing, Weak Reference Objects are not the default type of Reference objects and they must be explicitly specified when used. a weak reference to an object is most likely used to clean the object in the next garbage collection process. A weak reference can be created as follows:
WeakReference<int arrayResult> reference = new WeakReference<>(new int);
3. Soft referencing
Soft referencing is used for memory-sensitive scenarios, The soft referred objects will be only garbage collected when your application is running low on memory. Therefore, as long as there is no critical need to free up some space, the garbage collector will not touch soft referred objects.
Similar to weak references, a soft reference is created as follows:
SoftReference<int arrayResult> reference = new SoftReference<>(new int);
4. Phantom referencing
The objects which are being referred by phantom references are eligible for garbage collection but before deletion, JVM puts them in a queue called ‘reference queue’. It is done after calling the finalize() method on them, that is why the .get() method of such references always returns null as the object is no longer in the heap.
What is garbage collection?
Garbage collection is an integral part of memory management in Java. A garbage collector automatically finds the unused objects in the heap and deletes them to free up memory.
Garbage collectors follow the following series of steps.
- First, the unreferenced objects are identified in the heap and are marked as ready for garbage collection.
- In the next step, marked objects are then removed from the heap.
- The last step is optional where available memory is compacted after the garbage collection process so that the remaining objects are in a contiguous block at the start of the heap.
The compaction process makes it convenient to allocate memory to new objects in a sequence after the block of memory is allocated to existing objects.
Following are some of the important points around memory management in Java regarding the Garbage collection process,
- The garbage collection process is always triggered automatically by Java and it is totally up to Java when and whether or not to start the garbage collection process. A developer may explicitly call the system.gc() method but it is just like asking Java to run the process, if feasible.
- It can be expensive in terms of resource usage as when the garbage collector runs, all threads in your application are paused until it is completed.
- It is a way more complicated process than it seems as automatic object removal includes a lot of implicit steps.
Types of garbage collectors
being a quite complex process, JVM has three types of garbage collectors. By default, Java itself chooses the garbage collector type to be used depending on the hardware but the java developers have the option to choose which one should be used.
Following are the three types of Garbage collectors,
1. Serial GC
It is a single thread collector, mostly used for small applications with minor data usage.
2. Parallel GC
As the name suggests, the parallel GC uses multiple threads at a time to perform the garbage collecting process. It is also known as a throughput collector and due to its better performance, it is a better choice for applications with moderate data usage.
3. Mostly concurrent GC
It is the most expensive garbage collector of all as it pauses all threads when executed. The reason why it is “mostly” concurrent is that it does not work concurrently with the applications. There is a specific period for which the threads are paused which is kept as short as possible to achieve the best GC performance and keep it least expensive.
There are 2 further types of mostly concurrent GC:
1.1 Garbage First – It has high throughput with a reasonable amount of application pause time.
1.2 Concurrent Mark Sweep – In this GC, the least application pause time is prioritized and is kept to a minimum as much as possible.
Some tips for better memory management in Java
- To keep the memory footprint to a minimum, limit the scope of the variables as much as you can. Keep in mind that whenever the top scope from the stack is popped, the references from that scope are lost which could make objects eligible for garbage collecting.
- Explicitly refer to null obsolete references. That will make these objects eligible for garbage collecting.
- Avoid finalizers as they slow down the process instead, instead use phantom references for cleanup work.
- Always go with weak or soft references when required instead of default strong references.
- JVisualVM also offers the option to make a heap dump at certain points. Use that to analyze how much memory is occupied by every class.
Understanding how memory management works in Java will give you the advantage of writing efficient and optimized programs. You can now configure your JVM using the most suitable configuration for your running application and identify memory issues and leaks easily.
See Also: Top Java Developing Tools And Software