top

Search

Java Tutorial

.

UpGrad

Java Tutorial

Finalize Method in Java

Introduction

Finalize method executes a code of statements/closed operation. Although garbage collection in Java is automated, you may need to perform some manual clean-up operations before garbage collection is done. This is where you will need to use the finalize() method. 

In Java, the garbage collector only collects objects created by new keywords. When new keywords do not create the objects, the garbage collector uses finalize method to perform clean-up processing.

Overview

In this tutorial, you will learn about the finalize method in Java and its utility to the programmer. We will start by seeing how the garbage collector works and how the garbage collection and finalize() method in Java work in conjunction. Further, we will understand the cleanup activity of finalize() method, finalization, etc., while looking at the syntax of finalize() Method in Java. 

We will also look at how the finalize() method works in various scenarios and the lifetime of a finalized object in Java. We will also look at the alternatives of finalize in Java and understand how relying solely on finalize in Java may not be wise.

Garbage Collector in Java

Whenever an object is unreferenced in Java programming, the garbage collector automatically reclaims the unused runtime memory. This has two benefits: 

  • It makes Java memory efficient

  • You need not use any manual operations

Now, we will see how this works by the following three ways to unreferenced objects:

i) By anonymous objects

import java.util.ArrayList;
public class AnonymousObjectsExample {
    public static void main(String[] args) {
        // Creating and using an anonymous object of StringBuilder
        String result = new StringBuilder("Hello, ")
                .append("John")
                .append("!")
                .toString();
        System.out.println(result);
        // Creating and using an anonymous object of ArrayList
        int sum = new ArrayList<Integer>() {{
            add(10);
            add(20);
            add(30);
        }}.stream()
          .mapToInt(Integer::intValue)
          .sum();
        System.out.println("Sum: " + sum);
    }
}

ii) By nulling reference

public class NullReferenceExample {
    public static void main(String[] args) {
        // Creating an object
        MyClass obj = new MyClass();
        // Nulling the reference
        obj = null;    
        // Perform some other operations
        // ...
    }
}
class MyClass {
    // A class definition
    // ...
    // You can include some class members and methods here
    // ...
    // Override finalize() method to observe garbage collection
    @Override
    protected void finalize() throws Throwable {
        System.out.println("Finalizing an instance of MyClass");
    }
}

iii) By assigning a reference to another object

public class ReferenceAssignmentExample {
    public static void main(String[] args) {
        // Creating objects
        MyClass obj1 = new MyClass();
        MyClass obj2 = new MyClass();
        // Assigning reference to another object
        obj1 = obj2;
        // Perform some other operations
        // ...
    }
}
class MyClass {
    // A class definition
    // ...
    // You can include some class members and methods here
    // ...
    // Override finalize() method to observe garbage collection
    @Override
    protected void finalize() throws Throwable {
        System.out.println("Finalizing an instance of MyClass");
    }
}

How Does the finalize() Method Work With Garbage Collection?

As it was mentioned earlier, this method is used before garbage collection. Always remember that the finalize() method comes protected under the object class, and it is defined as protected void finalize(){}

We will see how this works with an example:

public class FinalizeMethodExample {
    public static void main(String[] args) {
        // Creating objects
        MyClass obj1 = new MyClass();
        MyClass obj2 = new MyClass();
        // Nullifying references
        obj1 = null;
        obj2 = null;
        // Requesting garbage collection
        System.gc();        
        // Perform some other operations
        // ...
    }
}
class MyClass {
    // A class definition
    // ...
    // You can include some class members and methods here
    // ...
    // Override finalize() method
    @Override
    protected void finalize() throws Throwable {
        try {
            System.out.println("Finalizing an instance of MyClass");
            // Perform any necessary cleanup or finalization tasks
            // ...
        } finally {
            super.finalize();
        }
    }
}

In this example, we create two instances of the MyClass class: obj1 and obj2. Both objects are allocated in a heap and are initially reachable through their respective reference variables.

Next, we nullify the references obj1 and obj2 by assigning null to them. This makes the objects eligible for garbage collection because no active references are pointing to them.

After nullifying the references, we explicitly request garbage collection by calling System.gc(). This suggests to the JVM that it's a suitable time to perform garbage collection and reclaim the memory occupied by the ‘unreachable’ objects.

During the garbage collection, the JVM identifies the objects without active references and invokes the finalize() method on each. In the MyClass class, we have overridden the finalize() method to include any necessary cleanup or finalization tasks. In this example, we simply print a message indicating when the objects are finalized.

It's important to note that the finalize() method is not guaranteed to be called immediately after an object becomes eligible for garbage collection. The JVM determines when to run the finalization process based on its own algorithms and memory management strategies. The finalize() method is called just before the object is garbage collected, allowing it to perform any necessary cleanup operations.

What Is the Cleanup Activity?

The cleanup activity is done by the finalize() method in Java. To understand cleanup activity, consider the following scenario:

Let us consider that your object is not created using new. But, the garbage collector can only recognize objects created by new keywords. In such cases, you will need to use the finalize() method, which helps to define your class. 

Before the garbage collector goes on to release the storage that is taken up by your object, it calls upon finalize(), and only after the following garbage collection passes the memory will be reclaimed by it.

Final vs. Finally vs. finalize() in Java 

Final

Finally

Finalize()

It is a type of keyword in Java that does not allow any change in the value of a variable, prevents any method from overriding, and restricts any extension of a class or the formation of a subclass of a superclass.

It is a block used in Java for the execution of the code that it contains regardless of whether an exception is thrown.

It is a method in Java that helps in cleanup processing on your object before the garbage collector can reclaim it.

When to Use finalize() Method in Java?

Finalize() Method in Java might be used in the following scenarios:

  • Releasing system-level resources:
    Suppose your object uses any system-level resources, you can use finalize() Method to free up these resources before the garbage collector reclaims your object.

  • Releasing external resources:
    If your object has an external resource such as file handles or database connections, you can use the finalize() Method to release these resources before your object can be garbage collected.

  • Implementing custom clean-up procedures:

You can use finalize() Method to provide a custom clean-up procedure to your object

Why finalize() Method is used?

You can use finalize() method for the following reasons:

  • You can do clean-up operations on your object before the garbage collector reclaims it

  • It protects the overlooked resources used by your object and ensures its release before garbage collection.

  • It helps in profiling and debugging by letting you check when the garbage collector is reclaiming your objects.

Overriding in Java

Overriding provides subclasses with the ability to provide for its method implementation defined in its superclass.

How To Override finalize() Method?

To override the finalize() method in a Java class, follow these steps:

  • Declare a method named finalize() with the protected access modifier in your class. The protected modifier allows the method to be accessed by subclasses and classes within the same package.

  • Add the @Override annotation above the method declaration to ensure you override the finalize() method from the superclass (which is Object).

  • Specify that the method throws Throwable. This is required because the finalize() method throws Throwable, including Exception and Error subclasses.

  • Implement the desired logic inside the finalize() method. This logic typically includes cleanup or finalization tasks that need to be performed before the object is garbage collected. Examples of such tasks include releasing resources, closing connections, or performing other cleanup operations.

Example:

public class MyClass {
    // Class members and methods go here
    @Override
    protected void finalize() throws Throwable {
        try {
            // Perform cleanup or finalization tasks here
            // ...
        } finally {
            super.finalize();
        }
    }
    public static void main(String[] args) {
        // Create an instance of MyClass
        MyClass myObject = new MyClass();
        // Perform some operations
        // ...
        // Set the reference to null to make the object eligible for garbage collection
        myObject = null;
        // Request garbage collection
        System.gc();
        // Perform some other operations
        // ...
    }
}

How Does the finalize() Method Works in Different Scenarios?

It's important to note that the finalize() method is generally discouraged for critical resource cleanup or finalization tasks. It's recommended to use explicit resource management techniques, such as try-with-resources, to ensure proper resource release and cleanup.

Let us look at how finalize() method works in two different scenarios:

  • Object without finalize() method overridden:

If an object does not have the finalize() method overridden, the garbage collector will skip calling any specific finalization logic for that object. In this case, the object will still be eligible for garbage collection like any other object, but it won't have a chance to perform any custom cleanup tasks before being reclaimed.

  • Finalization and object resurrection:

In some scenarios, an object can be "resurrected" during its finalization process. If an object's finalize() method resurrects the object by creating a new strong reference to it or adding it to some reachable data structure, the object will become reachable again and will not be garbage collected. This scenario is generally discouraged, as it can lead to unpredictable behavior and interfere with the normal garbage collection process.

Lifetime of the Finalized Object in Java

The lifetime of your object in Java employing finalize() method passes through several stages. These stages are as follows:

  • Your object attains the “live” phase once you have created it. 

  • Once your object gets no references from the program, it is on the way to being reclaimed by the garbage collector.

  • When the garbage collector identifies your object, it calls upon the finalize() method before reclaiming it. 

  • Upon completion of the finalize() method, the object is released from the memory and translocated into the “unreachable” phase.

Avoiding Finalizers

Disadvantage of Finalizers

The disadvantages of Finalizers are as follows:

  • You cannot assign a particular time to call for this method. Hence it can get potentially risky to handle sophisticated operations.

  • You can often face issues regarding performance 

  • It can be difficult at times for you to understand the lifetime of your object embedded in a program

Alternatives for finalize()

Some of the alternatives to finalize() method are as follows:

  • You can use the shut-down hook to perform any cleanup processing. 

  • You can use a reference queue to release objects by performing cleanups when they become obsolete.

  • You can use the try-with-resources statement, which came into existence in Java 7. This statement helps you to easily handle resources that need to be released when they become obsolete. 

  • You can use the PhantomReference class, which has a similar action to the previously mentioned alternatives.

Conclusion

We learned that by using finalize() method, you can manually perform cleanup operations before the garbage collector can reclaim it. We explored the various advantages and disadvantages of using finalize() method, such as potential risks and performance issues, and also got to know some of the alternatives for finalize(). However, it is best not to heavily depend upon finalize() method and to use the alternatives as suggested for better and optimized performance of your program. 

Learn more about finalize() method and other Java concepts by enrolling in a professional course at online learning platforms like upGrad. 

FAQs

1. When is the finalize() method called?

Finalize() method is called before reclaiming your object by the garbage collector.

2. How many times can the finalize() Method be called for the same object?

You can use finalize() Method only once for an object.

3. Can I override finalize() Method in a subclass?

Yes, you can override the finalize() method in Java for a specific implementation. 

Leave a Reply

Your email address will not be published. Required fields are marked *