Page Content

Tutorials

What is Garbage Collection in Java? With Code Examples

Garbage Collection in Java

Compared to languages like C and C++, Java’s memory management strategy is one of its most basic characteristics, which greatly simplifies programming. Through a mechanism called Garbage Collection (GC), Java offers automatic memory management, eliminating the need for programmers to manually allocate and free dynamic memory.

It can be challenging and time-consuming to manage memory in conventional programming environments like C or C++. When an object is no longer needed, programmers frequently have to explicitly release the memory using a delete operator after manually allocating it for it using operators like new. Because of memory management errors, such as failing to release allocated memory (which causes memory leaks) or trying to release memory that is still being used, this manual method usually results in program failures.

Java automates memory allocation and deallocation, hence removing these issues almost entirely. One part of the Java runtime system that handles locating and recovering memory from unnecessary objects is called the Garbage Collector (GC). Java’s garbage collection works on the fundamental tenet that an object is presumed to be unnecessary when there are no references to it, allowing the memory it takes up to be recovered. Subsequent allocations can then use this recycled memory. Additionally, garbage collection is supported in multithreaded programming, where the garbage collector thread is a daemon thread that serves user threads in the background.

Benefits of Garbage Collection in Java
Benefits of Garbage Collection in Java

There are several advantages to automatic garbage collection.

  • Enhanced Robustness: Java’s enhanced robustness is a result of its significant reduction in program failures brought on by memory management issues.
  • Prevention of Traditional Memory Leaks: GC avoids the traditional issue of unreferenced data continuing to occupy memory by automatically recovering information. This also has to do with the idea that “immutable objects” and “empty references” facilitate garbage collection.
  • Developer Productivity: Programmers can concentrate more on the logic of the application rather than the finer points of memory management, which increases developer productivity.
  • Improved Efficiency: It’s a crucial component for creating reliable apps and adds to Java’s overall effectiveness.

Trash collection happens in the background, transparently and intermittently. It’s impossible to predict exactly when it will happen. Generally speaking, it only operates when there are items to recycle and a requirement to do so for efficiency. The Java Virtual Machine is only advised to use the System.gc() function to recycle unneeded objects; there is no assurance that it will execute or that a complete garbage collection will take place right away. Generally speaking, calling System.gc() is not a good option for performance reasons because a full GC can be costly and may cause memory locality issues.

What are frequently referred to as “memory leaks” can still occur in Java programs even with automatic garbage collection. When an object is no longer required by the program but is still accessible by running threads because of persistent references, this happens (for example, in a cache or collection that expands endlessly without adequate cleanup). In these situations, the items are theoretically still referenced, therefore the GC cannot recover the memory. Maximum-size caches and WeakHashMap are two techniques that can help reduce these contemporary types of memory leaks.

Finalize() Method

Java has a mechanism called finalisation with the finalize() method, which works in tandem with garbage collection. To define what will happen right before an object is finally destroyed by the garbage collector, this method can be specified in a class.

In general, the finalize() method looks like this:

protected void finalize() throws Throwable {
    // finalization code here
}

The protected keyword restricts other users’ access to finalize() method.

Nevertheless, the finalize() method has a number of important restrictions and constraints that make it typically not recommended for use in the majority of applications.

  1. No Execution Guarantee: The most significant drawback is that Java makes no guarantees regarding the execution of finalize(). finalize() may never be invoked for some objects if a program ends before the garbage collector is able to run. The garbage collector doesn’t have a set schedule; it runs periodically to look for items that haven’t been accessed.
  2. Not a Destructor: finalize() is not comparable to a destructor in C++. When an object exits scope, C++ destructors are always invoked, whereas finalize() is only performed right before trash collection, which occurs infrequently.
  3. Performance Impact: Waste collection delays and performance overhead may be introduced by finalisers.
  4. Resource Management: Because there are no execution guarantees, using finalize() to promptly clean up resources (such as shutting down file streams or network connections) is troublesome. They provide an example of FileInputStream closing the resource as a last resort using finalize(), but they make it clear that this is not the best option.
  5. Resurrection: In its finalize() method, an object can “resurrect” itself by making itself reachable once again (for example, by assigning this to a static field). The object won’t be trash collected in this case, and if it becomes unreachable again, its finalize() method won’t be executed.

The try-with-resources statement (added in JDK 7) is the suggested method for resource management that is resilient. When the try block is exited, this construct makes sure that resources (objects implementing java.lang.AutoCloseable) are automatically closed, regardless of whether an error occurred. This offers a far more robust cleanup guarantee than finalize(). To explicitly close resources in older Java versions (before to JDK 7), a finally block is utilised.

Code Example: Demonstrating finalize()

To demonstrate finalize() in action, we can create a large number of objects to put pressure on the Java Virtual Machine’s memory, thereby prompting garbage collection to occur.

class FDemo {
    int x;
    // Constructor to assign an ID to the object
    FDemo(int i) {
        x = i; // Assign an ID for tracking
    }
    // This method is called by the garbage collector just before an object is recycled
    @Override // It's good practice to use @Override for clarity
    protected void finalize() throws Throwable {
        System.out.println("Finalizing object with ID: " + x);
        super.finalize(); // Call the superclass finalize method, if applicable
    }
    // This method creates an object that immediately becomes eligible for garbage collection
    void generator(int i) {
        FDemo o = new FDemo(i); // Creates a new FDemo object
        // The local variable 'o' quickly goes out of scope,
        // making the FDemo object eligible for GC
    }
}
public class FinalizeDemo { // Renamed to FinalizeDemo to avoid potential conflicts
    public static void main(String args[]) {
        int count;
        FDemo ob = new FDemo(0); // Create an initial FDemo object
        System.out.println("Starting object generation...");
        /*
         * This loop rapidly creates and discards objects,
         * putting pressure on memory and increasing the likelihood
         * that the garbage collector will run.
         * The exact number of iterations might need adjustment
         * depending on the JVM, operating system, and available memory
         * to reliably see finalize() messages.
         */
        for (count = 1; count < 1000000; count++) { // Increased count for better demonstration
            ob.generator(count); // Create and discard FDemo objects
        }
        System.out.println("Object generation complete. Waiting for GC...");
        // Explicitly suggest garbage collection.
        // Note: This is merely a suggestion; the JVM is not guaranteed to act immediately [36].
        System.gc();
    }
}

Code Explanation and Output

Each object is uniquely identified by the integer x that is assigned by the constructor of the FDemo class. The ID of the object being finished is printed in a message by its finalize() method. FDemo‘s generator() method generates a new FDemo object. The object that o references is immediately available for trash collection when generator() finishes running since o is a local variable.

A loop iterates a lot (one million times, for example) in FinalizeDemo main method. Every iteration generates and essentially discards a new FDemo object by using ob.generator(count). The garbage collector must run in order to recover memory because of the heavy strain this quick allocation and de-referencing of objects puts on the Java Virtual Machine’s memory heap. “Finalising object with ID: X” and other similar messages are printed to the console while the garbage collector runs and reclaims these objects, invoking their finalize() methods.

The JVM is suggested to execute garbage collection by the explicit call to System.gc() at the end of the main method, which occasionally speeds up the appearance of these finalize() notifications, albeit their execution is not assured.

Example Output (Please note that actual output will vary significantly based on your system’s JVM, operating system, and available memory):

Starting object generation...
Finalizing object with ID: 123456
Finalizing object with ID: 123457
Finalizing object with ID: 123458
... (many more "Finalizing" messages will appear sporadically)
Finalizing object with ID: 500000
...
Object generation complete. Waiting for GC...
Finalizing object with ID: 999997
Finalizing object with ID: 999998
Finalizing object with ID: 999999

This result shows that for objects that are being reclaimed, the garbage collector calls finalize() methods. However, there is no guarantee that the finalize() method of each object will be called before the program ends, and the precise timing and sequence are uncertain.

Index