0% found this document useful (0 votes)
9 views13 pages

Understanding Java Garbage Collection

Garbage collection in Java automatically reclaims memory from objects that are no longer reachable without requiring explicit deallocation. An object progresses from creation to being in use, then invisible if no longer reachable, then unreachable and eligible for collection. The garbage collector traces references from roots like static variables and stacks to determine reachable objects. Reference types like soft and weak references also help tune garbage collection behavior.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views13 pages

Understanding Java Garbage Collection

Garbage collection in Java automatically reclaims memory from objects that are no longer reachable without requiring explicit deallocation. An object progresses from creation to being in use, then invisible if no longer reachable, then unreachable and eligible for collection. The garbage collector traces references from roots like static variables and stacks to determine reachable objects. Reference types like soft and weak references also help tune garbage collection behavior.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Garbage Collection in Java

see web pages for links


Two extremes
● Programmer not responsible at all for memory
management
● Programmer has to be very careful to please GC

● but:
● need to understand GC for large-scale programs
● (potentially) huge impact on speed
GC guarantees
● Java Language specification (JLS) rather vague
● Java Virtual Machine Spec (JVMS):
– heap is created at JVM startup
– heap storage for objects is automatically reclaimed
(GC); no explicit deallocation
– no specific mechanism specified
– why?
Object Lifecycle
● Creation
● In use (strongly reachable)
● Invisible
● Unreachable
● Collected
● Finalized
● Deallocated
Creation
● allocate space
● begin object construction
● call superclass constructors (recursively)
● run instance initializers and instance variable
initializers
● execute the body of the constructor
In use
● in use: held by at least one strong reference
● all references are strong (unless we explicitly use one of: soft,
weak, or phantom refs, see later)
public class CatTest { Heap
static Vector catList = new Vector();
[Link]
static void makeCat() {
Object cat = new Cat();
[Link](cat); Vector
Stack
}
main
public static void main(String[] arg) { makeCat
makeCat(); Cat
// do more
}}
Invisible
● no strong reference left that is accessible to the
program, but there are still references: local vars
that have gone out of scope:
public void run() {
try {
Object foo = new Object();
[Link]();
} catch (Exception e) {
// whatever
}
while (true) { // ... loops forever
Unreachable
● no more direct or indirect strong references from
the rootset to an object ==> candidate for
collection
● rootset:
– temp vars on the stack (all threads)
– static vars (all loaded classes)
– all references coming from native code (JNI)
● Note: circular refs are no problem with this def
Example
public void buildDog() { Stack Heap
Dog newDog = new Dog();
main Dog
Tail newTail = new Tail(); ...
[Link] = newTail; buildDog Tail
[Link] = newDog;
}
Stack Heap

main Dog
...
Tail
Collected, Finalized, Deallocated
● if the collector finds an object unreachable:
– if it has a “finalize” method: mark for finalization (i.e.
final deallocation will be delayed)
● finalizers have been run ==> finalized
– no guarantees about “when”, may actually not even
run before termination
– finalizers are rarely a good idea
– beware of “resurrection”
● deallocation: reuse space (again, whenever)
Reference objects
● [Link] to help tune GC, prevent leaks
● soft, weak, and phantom refs, reference queues
– soft refs for implementing memory-sensitives caches
– weak refs for mappings where keys or values may be
reclaimed
– (phantom refs for final cleanup, better than finalizers)
Reachability
● strongly reachable: only strong refs from rootset
● softly: not strongly, but through live soft ref
– will only collected (+ set ref to null) if space is scarce
● weakly: neither strong nor soft, but live weak ref
– will be collected and ref is set to null
● (phantom: neither strong, weak, nor soft, and has
been finalized)
● else: unreachable, ==> reclaim space
Example
Stack Heap Heap Heap

[Link] [Link] [Link] [Link]

WeakReference WeakReference WeakReference

Dog Dog null

Tail Tail

Thread active Thread finished after GC

Common questions

Powered by AI

Relying on finalizers for object cleanup is problematic due to their non-deterministic nature, as their execution time or order is not guaranteed, and they may not run at all before program termination. This uncertainty can lead to resource leaks and inconsistent states. Additionally, improper use of finalizers can lead to performance issues and complexities like 'resurrection.' Alternatives to finalizers include using try-with-resources or explicit cleanup methods that provide more predictable and reliable resource management, ensuring timely deallocation of resources .

Circular references do not hinder Java's garbage collection due to the use of reachability analysis rather than reference counting. The garbage collector considers objects with strong references from the rootset, meaning if a group of objects only references each other and not the rootset, they are collected. This allows the collector to reclaim memory even when circular references exist, as long as they are not reachable from the rootset, avoiding common pitfalls associated with reference counting schemes .

The rootset in Java garbage collection consists of all references that the garbage collector starts from during its reachability analysis. These include temporary variables on the stack for all threads, static variables in loaded classes, and references from native code. Objects directly or indirectly reachable from the rootset are considered strongly reachable and are therefore not candidates for collection. The rootset's significance lies in its role as the initiation point for identifying live objects, directly affecting the determination of reachability and optimization of memory management .

'Resurrection' in Java occurs when a finalizer re-establishes a strong reference to an object, preventing it from being collected and allowing it to continue existing after finalization. This practice is discouraged because it can lead to unpredictable behavior, hinder garbage collection efficiency, and increase the risk of memory leaks. The unpredictability comes from the uncertain order and timing of finalizer execution, and resurrected objects can complicate the understanding and management of memory, possibly leading to resource exhaustion .

When a thread finishes execution in Java, all local variables on its stack become unreachable, releasing their references. This causes objects that were only reachable through that thread's stack to become candidates for garbage collection. Since those references are no longer in the rootset, the garbage collector can reclaim the memory associated with such objects if they do not have references elsewhere, effectively managing memory as threads complete their tasks .

The Java garbage collector determines an object is unreachable when there are no more strong references to it from the rootset, which includes temporary variables on the stack, static variables from loaded classes, and references from native code. Unreachable objects are candidates for collection. This has implications for memory management in large-scale programs because developers need to understand how reachability works to optimize memory usage and avoid memory leaks. Mismanagement can lead to increased garbage collector activity, potentially causing performance issues due to high overhead from frequent collections .

An object's lifecycle in Java includes creation, in use, invisible, unreachable, collected, finalized, and deallocated stages. During creation, space is allocated and initializers are run. An object in use is strongly reachable. If it becomes invisible, no strong reference is left accessible. Unreachable means it has no references from the rootset, making it eligible for collection. If collected and has a finalize method, it is marked for finalization, although finalizers are not guaranteed to run promptly. Finally, deallocation allows the space to be reused. Understanding these stages helps in tuning garbage collection and avoiding unintended memory retention .

Java manages object finalization by first collecting objects identified as unreachable. If an object has a finalize method, it is flagged for finalization, delaying its final deallocation. Finalizers allow for additional cleanup before an object is discarded, but their non-deterministic timing can impact performance, with potential delays in resource reclamation. This can lead to inefficient memory use and make performance tuning challenging. Thus, using finalization judiciously is critical, and alternative cleanup strategies should be considered for managing resources effectively .

Soft, weak, and phantom references allow fine-tuning memory management in Java. Soft references are used for memory-sensitive caches, retained as long as memory is sufficient, but collected if memory is low. Weak references are used for mappings where the key/value may be reclaimed, immediately collected after reference goes unreachable. Phantom references are used for final cleanup after an object becomes unreachable and finalized, providing a more reliable finalization mechanism than finalizers. By using these reference types, programs can prevent memory leaks while optimizing performance by controlling the lifecycle of objects with more precision .

The Java Virtual Machine Specification (JVMS) does not specify a particular garbage collection mechanism to allow JVM implementations the flexibility to choose the most appropriate and efficient algorithms for their specific environments and hardware configurations. This modular approach provides the advantage of optimizing performance across diverse platforms. However, it also introduces variability in garbage collection behavior across different JVMs, making it challenging for developers to predict performance characteristics .

You might also like