Non-deterministic: Java Garbage Collection (Theory, Operation)

Control Flow  «Prev  Next»


Lesson 8Garbage collection
ObjectiveDescribe how the Java garbage collector works.

Unreachable objects during Garbage Collection

Objects in Java are always allocated on the heap. The heap is an area of memory that is used for dynamically allocated memory, such as objects. In Java, objects are allocated in a program and then released by the JVM. This release of memory is called garbage collection and performed automatically by the JVM. An application has little control over this process. The primary benefit of this technique is the minimization of memory leaks. A memory leak occurs when memory is dynamically allocated but is never released. This has been a common problem with languages such as C and C++, where it is the responsibility of the programmer to manage the heap. A memory leak can still occur in Java if an object is allocated but the reference to the object is never released when the object is no longer needed.
  • Object becomes inaccessible to the Program
    During the course of a program's execution, it is possible for objects used by the program to become inaccessible to the program. These objects are referred to as unreachable objects. [1] For example, in the following program, the Vector object created in line 5 becomes unreachable at line 9 because it is no longer referenced by any program variables.
    import java.util.*;
    public class GarbageExample {
     static Vector<String> v1, v2;
      public static void main(String[] args) {
       v1 = new Vector<String>(); // line 5
       v2 = new Vector<String>();
       v1.addElement("this");
       v2.addElement("that");
       v1 = v2;
       System.out.println(v1.elementAt(0));
     }
    }
    /* 
    Program Output 
    that
    */
    

Java Lambdas and Parallel Streams

Object becomes Unreachable

When an object becomes unreachable, it is no longer used by the program and becomes eligible for garbage collection[2].
The Java runtime system uses a process known as garbage collection to recover the memory used by unreachable objects. The Java garbage collector consists of a background thread that is executed by the JVM. This thread monitors the objects that are used by Java programs and identifies when objects become unreachable. When the garbage collector[3] finds an unreachable object, it uses the following process to free the resources used by the object:

  1. If the object has a finalize() method, then the garbage collector invokes the object's finalize() method.
  2. If the object's finalize() method has already been invoked, then the garbage collector permanently deletes the object.

The operation of the garbage collector.
The operation of the garbage collector


The garbage collector is subject to the thread scheduling algorithm of the local operating system, and its actual operation is non-deterministic. [4]This means that you cannot predict the following:
  1. When or if the garbage collector will execute
  2. When or if the garbage collector will identify an object as unreachable
  3. When or if an object will be garbage collected

Avoid "memory leaks" without using "garbage collection"?

In Java, avoiding memory leaks without relying on garbage collection (GC) is a challenging task because the language is designed with garbage collection as a core memory management feature. However, there are some practices and techniques that can minimize the risk of memory leaks without explicitly depending on GC:
  1. Proper Resource Management
    • Use the try-with-resources statement for managing resources like file streams, database connections, and sockets. This ensures resources are closed automatically, even in the event of exceptions.
                try (FileInputStream fis = new FileInputStream("file.txt")) {
                  // Process file
                } catch (IOException e) {
                  e.printStackTrace();
                }
              
      By releasing resources explicitly, you prevent resource leaks.
  2. Weak References
    • Use weak references for objects that are not crucial for the application's core logic, like cache entries. Weak references do not prevent an object from being garbage collected.
                WeakReference<Object> weakRef = new WeakReference<>(new Object());
              
  3. Avoid Long-Lived References
    • Be cautious with static fields or long-lived collections that hold references to objects. These can prevent the objects from being eligible for garbage collection.
                private static List<Object> cache = new ArrayList<>();
              
      Instead, ensure references are removed when no longer needed:
                cache.clear();
              
  4. Event Listener Management
    • Deregister event listeners when they are no longer needed to avoid retaining unnecessary references.
                someEventSource.removeListener(listener);
              
  5. Thread Local Variables
    • Be careful with ThreadLocal variables. Clear them explicitly when done.
                ThreadLocal<Object> threadLocal = new ThreadLocal<>();
                threadLocal.set(null); // Explicitly clear
              
  6. Avoid Circular References
    • Circular references are not typically an issue in Java due to GC, but if using custom memory management, ensure that objects referencing each other are released explicitly.
  7. Memory Pools and Manual Management
    • For advanced cases, implement custom memory pools or object pooling. However, this adds complexity and can lead to manual memory management bugs.
                ObjectPool<MyObject> pool = new ObjectPool<>(MyObject::new, 10);
              
  8. JVM Tools and Analyzers
    • Use tools like VisualVM or JProfiler to identify and mitigate memory leaks during development.

Limitations Despite these practices, Java still fundamentally depends on garbage collection for automatic memory management. Any effort to completely avoid GC would require manual tracking and freeing of memory, akin to languages like C or C++. This goes against Java's design philosophy and can make applications more error-prone and less maintainable. In essence, while you can minimize memory leaks through disciplined coding and resource management, entirely avoiding the use of GC would be impractical and counterproductive in Java.

[1] Unreachable object: An object that is no longer accessible to a program.
[2] Eligible for garbage collection: An object that is unreachable is eligible to be destroyed and have its resources reclaimed by the garbage collector.
[3] Garbage collection: The process by which the memory occupied by unreachable objects is reclaimed.
[4] Non-deterministic: Cannot be predicted or determined.

SEMrush Software