Memory Leak Due to Time-Taking finalize() Method
All objects in Java implicitly inherit from java.lang.Object. This class contains multiple methods, which can be overridden in the child class.
Join the DZone community and get the full member experience.
Join For FreeAll objects in Java implicitly inherit from java.lang.Object
. This class contains multiple methods, which can be overridden in the child class.
One such overridable method is finalize()
. Before objects get garbage collected from memory, the JVM will execute the finalize()
. So, if you need to close any resources that were opened in the class (like backend connections, Files…), it can be done in this method.
However, this method has a few shortcomings; when it’s not carefully handled, it will result in an OutOfMemoryError. This post explains those in detail and how to address them.
Note: The finalize()
method has been deprecated in Java 9 due to several inherent flaws (one of them is exposed in this post). So, refrain from using the finalize()
method in your code, instead, refer to the solutions given in the ‘What are the alternatives to finalize()
methods?’ section of this post.
Poorly Implemented finalize() Method Results in OutOfMemoryError
Here is a sample program that simulates OutOfMemoryError
due to the improper implementation of the finalize()
method:
01: public class TimeTakingFinalizer {
02:
03: static class SlowFinalizerObject {
04:
05: private String largeString = generateLargeString();
06:
07: @Override
08: protected void finalize() throws Throwable {
09:
10: Thread.sleep(5000); // Simulating slow finalization
11: }
12: }
13:
14: public static void main(String[] args) {
15:
16: int count = 0;
17:
18: while (true) {
19:
20: new SlowFinalizerObject(); // Creating objects rapidly
21:
22: if (++count % 10 == 0) {
23: System.out.println("Created " + count + " objects.");
24: }
25: }
26: }
27:
28: private static String generateLargeString() {
29:
30: StringBuilder sb = new StringBuilder(5 * 1024 * 1024); // ~5MB
31: while (sb.length() < 5 * 1024 * 1024) {
32: sb.append("X");
33: }
34: return sb.toString();
35: }
36:}
Before continuing to read, please take a moment to review the above program closely. In line #3, SlowFinalizerObject
is declared. Whenever this object is instantiated, in line# 5, a huge string ‘XXXXXXXXXXXXXXXXX…’ is created and assigned to the largeString
instance variable. In line #8, the finalize()
method is implemented for this object. This method puts the thread to sleep for 5 seconds, to simulate a situation where this method takes a long time to complete. In line #20, SlowFinalizerObject
is continuously created because of the while(true)
condition in line #18.
In a nutshell, this program is continuously creating SlowFinalizerObject
and the finalize()
method of this object takes 5 seconds to complete. When this program is executed, it will result in java.lang.OutOfMemoryError: Java heap space
. However, if the finalize()
method is removed (i.e., line #8 – #11), this program can run continuously fine without any OutOfMemoryError
.
Why Does the Slow finalize() Method Result in OutOfMemoryError?
To find the answer to this question, we need to understand how the JVM executes the finalize()
method:
- Whenever an object is eligible for garbage collection, and if it implements the
finalize()
method, it will be added to an internal queue which is present in java.lang.Finalizer class. - For the entire JVM, there is one single
Finalizer
thread, that will execute thefinalize()
method of all the objects that are present in the internal queue. - Say if the
finalize()
method takes a long time to complete (as in this case), theFinalizer
thread will get stuck, and the internal queue will start to build up quickly. - When the queue starts to grow beyond the allocated memory size, an
OutOfMemoryError
will be thrown.
How to Diagnose the finalize() Method Created by Memory Leak?
You want to follow the steps highlighted in this post to diagnose the OutOfMemoryError: Java Heap Space. In a nutshell, you need to do:
1. Capture Heap Dump
You need to capture a heap dump from the application, right before the JVM throws an OutOfMemoryError
.
In this post, 8 options to capture the heap dump are discussed. You might choose the option that fits your needs. My favorite option is to pass the -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<FILE_PATH_LOCATION>
JVM arguments to your application at the time of startup.
Example:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/tmp/heapdump.hprof
When you pass the above arguments, the JVM will generate a heap dump and write it to /opt/tmp/heapdump.hprof
file whenever an OutOfMemoryError is thrown.
2. Analyze Heap Dump
Once a heap dump is captured, you need to analyze the dump. In the next section, we will discuss how to do heap dump analysis.
Heap Dump Analysis – finalize() Method
Heap dumps can be analyzed using various tools, such as HeapHero, JHat, and JVisualVM. Let’s analyze the heap dump captured from this program using the HeapHero tool.

Fig: HeapHero flags memory leak using ML algorithm
The HeapHero tool uses machine learning algorithms internally to detect whether any memory leak patterns are occurring in the heap dump. Above is the screenshot from the heap dump analysis report, flagging a major warning that java.lang.ref.Finalizer
class is occupying 95.61% of the overall memory. It’s a strong indication that the application is suffering from a memory leak, and it originates from the java.lang.ref.Finalizer
class.

Fig: Largest Objects section highlights java.lang.reg.Finalizer
The ‘Largest Objects’ section in the HeapHero analysis report shows all the top memory-consuming objects, as shown in the above figure. You can clearly notice that the java.lang.ref.Finalizer
class occupies 95.61% of memory.

Fig: Outgoing Reference section of java.lang.reg.Finalizer
The tool also gives the capability to drill down into the object to investigate its content. When you drill down into the java.lang.ref.Finalizer
class reported in the ‘Largest Object’ section, you can see all its child objects of this class.
From the above figure, you can notice the actual string ‘XXXXXXXXXXXXXXXXXXXXXXX…’ to be reported. Basically, this is the string that was added in line #5 of the above program. Thus, the tool helps you to point out the memory-leaking object and its source, with ease.
What Are the Alternatives to finalize() Method?
There are a few strategies to close the resources instead of the finalize()
method. They are:
1. Use try-with-resources
For objects holding external resources like files or streams, implement AutoCloseable
and use them within a try-with-resources block. This guarantees that cleanup logic is executed promptly when the block completes, reducing the chance of lingering memory.
2. java.lang.ref.Cleaner
Cleaner is a modern, safer replacement for finalize()
. It allows you to register a cleanup action that runs after an object becomes unreachable, without blocking garbage collection like finalize()
can.
3. java.lang.ref.PhantomReference
With PhantomReference
, you can monitor when an object is ready for collection. By using a reference queue, you can trigger cleanup code outside of the GC thread, avoiding the pitfalls of finalization delays.
4. Use Explicit Cleanup Methods
Design your API with a clear close()
or dispose()
method and document the need to call it. This gives developers full control over resource management and avoids the unpredictability of automatic finalization.
Conclusion
From this post, we can understand that a poorly implemented finalize()
method has the potential to bring down the entire application. By leveraging alternative strategies to finalize()
method and using tools like HeapHero for faster root cause analysis, you can protect your applications from hard-to-detect outages.
Published at DZone with permission of Ram Lakshmanan, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments