DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Languages
  4. Behind the Scenes of the Finalize() Method

Behind the Scenes of the Finalize() Method

Let's look under the hood to see how the finalize methods work, the role of the finalizer thread in the JVM, and how to avoid OOM errors.

Ram Lakshmanan user avatar by
Ram Lakshmanan
CORE ·
May. 10, 18 · Tutorial
Like (5)
Save
Tweet
Share
13.78K Views

Join the DZone community and get the full member experience.

Join For Free

“What is the purpose of the finalize() method?” is an often asked Java interview question. The typical answer to it is: “The usual purpose of the finalize() method is to perform cleanup actions before the object is discarded.” However, behind the scenes, finalize() methods are handled in a special way. A small mistake in finalize() has the potential to jeopardize entire application’s availability. Let’s study it in detail.

Behind the Scenes

Objects that have “finalize()” methods are treated differently during garbage collection than ones that don’t have it. During garbage collection, objects with “finalize()” methods aren’t immediately evicted from memory. Instead, as the first step, those objects are added to an internal queue of ‘java.lang.ref.Finalizer’. For the entire JVM, there is only one low priority JVM thread, ‘Finalizer’, that executes the “finalize()” methods of each object in the queue. Only after the execution of the “finalize()” method does the object become eligible for garbage collection.

Assume that if the application is producing a lot of objects that have “finalize()” methods and the low priority “Finalizer” thread isn’t able to keep up with executing those finalize() methods, then a significant amount of unfinalized objects will start to build up in the internal queue of ‘java.lang.ref.Finalizer’, which would result in a significant amount of memory wastage.

Sometimes, because of poor programming practices, the “Finalizer” thread may start to WAIT or BLOCK while executing the “finalize()” method. If the “Finalizer” thread starts to wait or block, then the number of unfinalized objects in the internal queue of ‘java.lang.ref.Finalizer’ will start to grow significantly, which would result in an OutOfMemoryError, jeopardizing the entire JVM’s availability.

Example

To illustrate this theory, we wrote a simple sample program.

public class SampleObject {
       public String data;
       public SampleObject(String data) {
             this.data = data;
       }
       @Override
       public void finalize() {
             try {
                  // Sleep for 1 minute.
                  Thread.currentThread().sleep(1 * 60 * 1000);
             } catch (Exception e) {}
       }
       public static void main(String[] args) {
             long counter = 0;
             while (true) {
                  new SampleObject("my-fun-data-" + counter);
                  System.out.println("created: " + counter++);
             }
       }
}


Basically, the ‘main()’ method of this class creates ‘SampleObject’ continuously. The interesting part of this program is the “finalize()” method. This method puts the currently executing thread (i.e. the ‘Finalizer’ thread) to sleep for 1 minute. This example illustrates a poor implementation of “finalize()” method.

When we ran the above program with a max heap size of 10 MB (i.e. -Xmx10M), it crashed with ‘java.lang.OutOfMemoryError’ after a few seconds of launch.

This program crashed with a ‘java.lang.OutOfMemoryError’ because only after the execution of the ‘finalize()’ method can SampleObject be evicted from memory. Since the ‘Finalizer’ thread is put to sleep, it can't execute the “finalize()” method at the rate in which the ‘main()’ method was creating new ‘SampleObjects’. Thus, the memory got filled up and program resulted in a ‘java.lang.OutOfMemoryError’.

On the other hand, when we commented out the “finalize()” method, the program ran continuously without experiencing any ‘java.lang.OutOfMemoryErrors’.

How to Diagnose the Problem?

Your application might contain hundreds, thousands, or millions of classes. It includes classes from 3rd-party libraries and frameworks. Now the question becomes, how will you identify “finalize()” methods that are poorly implemented? This is a tough question to answer. This is where heap dump analysis tools like HeapHero.io might come handy.

When heap dump was captured from the above program and uploaded to HeapHero.io, it generated this beautiful report with several sections. The section that is of interest to us is ‘Objects waiting for finalization’.

objects-waiting-finalize

Fig 1: Excerpt from the report generated by HeapHero.io

This section of the report shows the amount of memory wasted due to objects waiting for finalization of your application. In this hypothetical example, 7.66 MB i.e. 97.2% is the amount of memory that is wasted.

finalization-object-tree

Fig 2: Objects waiting for finalization

When you click on the hyperlink given under ‘What are the objects waiting for finalization?’, you will be able to see the objects waiting to be finalized. Basically, you will be able to see the object tree of “j.l.r.ReferenceQueue” (note that this is the queue of the ‘java.lang.ref.Finalizer’ object that holds the reference of all objects whose finalize() methods need to be executed). If you drill down the tree, it will show the objects that are sitting in the queue waiting to be finalized. Here you can see 2 types of objects that are sitting in the queue:

  1. com.petals.finalize.SampleObject.data occupying 56.8% of memory
  2. com.petals.finalize.SampleObject occupying 11.5% of memory

BINGO!! These are the objects that are created in our sample program!

Object (computer science) garbage collection Memory (storage engine)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • What Is JavaScript Slice? Practical Examples and Guide
  • Specification by Example Is Not a Test Framework
  • Rust vs Go: Which Is Better?
  • mTLS Everywere

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: