Memory Leak in Java
Tags: beginners, java, spring, programming
Published: 2022-08-30 10:30:00

**What is Memory Leak in Java ?**

A Java memory leak happens when an application holds  object references that are no longer required. These accidental object references prevent the built-in Java garbage collection mechanism from freeing up the memory consumed by these objects , which eventually leads to a fatal _**OutOfMemoryError**_.

**In short Memory Leak is** - Object reference which are no longer required , still present in HEAP memory and Garbage Collector is not able to remove them.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5cm1iuwpo5xssbb4j8nc.png)

**Most common scenarios where Memory Leak happens :** 

- Not properly use of Static Members.
- Unclosed resources.
- Adding Objects with no hashCode() and equals() into a HashSet.
- Excessive session objects.
- Poorly writing of custom Data Structure.

**Here we will discuss few of the above :**

<u>**1. Not properly use of Static Members:**</u>

Fields that have the static modifier in their declaration are called static fields or class variables. They are associated with the class, rather than with any object. When a variable is declared as static, then a single copy of the variable is created and shared among all objects at the class level. In Java, static fields have a life that usually matches the entire lifetime of the running application .Hence static members are related to Class , So Garbage collector can’t clean the memory space occupied by static members.

**Here is one example :**

You can see here I have created one static ArrayList member called list and used that variable to store the String at line 10 . At line 19 , I have called Garbage Collector . Here I will demonstrate, though I have called Garbage Collector at line 19, garbage collector will not be able to clean up the Memory space for it .  To do this I have added debug point on line 12, line 16, line 18, line 20 .

Lets execute the program :

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g6r8r4up5zp3o59ml3jp.png)

At Line 18 , Notice how, at the very beginning, all memory is, of course, free. Then, the iteration process runs and finishes – loading everything into the list (naturally this will depend on the machine you’re running the test on). We can see the spike in the Graph (Right side).


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5s3olx2uek9hjo6ggbys.png)

At line 20 ,After a full garbage collection cycle is triggered, and the test continues to execute, to allow this cycle time to run and finish. As you can see, the list is not reclaimed and the memory consumption doesn’t go down.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qiyrq6qtbrbs33k95r3r.png)

Let’s now see the exact same example, only this time, the ArrayList isn’t referenced by a static variable. Instead, it’s a non-static instance  variable that gets created, used and then garbage collected .


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rku6xhaupre3ntadomqe.png)

At Line 18 , Notice how, at the very beginning, all memory is, of course, free. Then,  the iteration process runs and finishes – loading everything into the list (naturally this will depend on the machine you’re running the test on). We can see the spike in the Graph (Right side).


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5hvwbhfjeew2qbofj4h7.png)

At line 20 ,After  a full garbage collection cycle is triggered, and the test continues to execute, to allow this cycle time to run and finish. You can see, Notice how the GC is now able to reclaim some of the memory utilized by the JVM. (Right side)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cuyom84erngih6ys6ity.png)

**<u>2. Unclosed streams:</u>**

Forgetting to close a stream is a very common scenario, and certainly, one that most developers can relate to. The problem was partially removed in Java 7 when the ability to automatically close all types of streams was introduced into the try-with-resource clause.


Let’s see how the memory of the application looks when loading a large file from an URL:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kb5hmqau03w69bpnlqca.png)

As we can see, the heap usage is gradually increasing over time – which is the direct impact of the memory leak caused by not closing the stream.
Let’s dig a bit deeper into this scenario because it’s not as clear-cut as the rest. Technically, an unclosed stream will result in two types of leaks – a low-level resource leak and memory leak.
The low-level resource leak is simply the leak of an OS-level resource – such as file descriptors, open connections, etc. These resources can also leak, just like memory does.

Of course, the JVM uses memory to keep track of these underlying resources as well, which is why this also results in a memory leak.

Here you can see Used Metaspace while starting execution of the Program.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/62ul5bskft5omx48yc1f.png)

Here you can see Used Metaspace while ending of execution of the Program.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iy9na6auc97cx2s4w2sv.png)

Here you can see the used heap memory


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4ptrq6n0ariu5u1jums.png)


We always need to remember to close streams manually, or to make a use of the auto-close feature. In this case, the BufferedReader will be automatically closed at the end of the try statement, without the need to close it in an explicit finally block.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cpke8xjkwoz2yp8bzkwn.png)

Here you can see Used Metaspace while starting of execution of the Program.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1w4n0h6ljl0wr7xc7vfo.png)

Here you can see Used Metaspace after ending of execution of the Program which is lesser than the previous program where we have not closed the stream .


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qcr129e1ut7ob0ssq9y6.png)

Here you can see the used heap memory , which is lesser than the previous program where we have not closed the stream .


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gybfgff8kkmk42znyx3w.png)

**<u>3. Adding Objects with no hashCode() and equals() into a HashSet:</u>**
A simple but very common example that can lead to a memory leak is to use a HashSet with objects that are missing their hashCode() or equals() implementations.
Specifically, when we start adding duplicate objects into a Set – this will only ever grow, instead of ignoring duplicates as it should. We also won’t be able to remove these objects, once added.

Here we have created one Class Country without hashCode() & equals() method .


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/86lgh3lhzz6ekrcv2c8h.png)

Here we have created one HashSet Object in which we can store Country objects . Now we are storing Multiple duplicate Country objects.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2qdb0x0labb2tf6ni1cv.png)

We can see  duplicate objects are added into a Set – this will only ever grow, instead of ignoring duplicates as it should.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/03qb3bzrtq0bogfqdumx.png)

We can see the heap memory usage here.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zl7sh6fj8a1h7bcxm6oa.png)

Now we overridden  equals() & hashCode() methods.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d46krg3xx5576r9wxznd.png)


Here we can see no duplicate objects added .


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wwn3v4jd0pkk2ge0ok9k.png)

Here we can see very less heap space has been used .


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jmtbphr34i4lwkhfu10z.png)

**So, here is the short note on how to prevent memory leak in java**

- Do not create unnecessary objects.
- Avoid use String concatenation and use StringBuilder.
- Do not store massive amount of data in the Session.
- Timeout the Session when no longer used.
- Avoid use of static members (if not required) because it lives entire life of the application.
- Always close the streams & any resources in the finally blocks.

Let's connect:
LinkedIn : https://www.linkedin.com/in/arpan-bandyopadhyay-bb5b1a54/

** reference - internet, youtube vlogs.