While Java’s garbage collection handles most memory-related tasks automatically, developers still need to be aware of certain patterns and practices that can lead to memory leaks. This article explores common scenarios where memory management requires special attention and provides guidelines for writing memory-safe code in Java applications. Patterns here were observed while developing applets, but they should be applicable to any Java application.
Static Objects
Static objects are treated as singletons and never collected from memory. They can be retained even if the app is destroyed on mobile platforms. If a static object holds reference to other objects, those objects will be retained as well.
In contrast, static methods are fine to use, as long as they’re not referencing other static fields in the class.
Stateless Lambdas
For every lambda, JVM generates a synthetic class. If the lambda is stateless (i.e., doesn’t hold any state outside its scope), JVM will make it a singleton that will be retained when the app is destroyed.
Lambdas can be either capturing or stateless, and understanding this difference is crucial as it affects their behavior. For simplicity and consistency, if you’re not sure, it’s recommended to use anonymous classes instead of lambdas, as anonymous classes have more predictable behavior.
Anonymous Classes
To avoid issues with stateless lambdas, it’s better to use Anonymous Classes. Anonymous Classes are “first-class citizens” in Java and are treated as any other object. For listeners, it’s safer to use Anonymous Classes.
However, before JDK 18, any Anonymous Class always holds a reference to the enclosing class. If an anonymous class is retained (e.g., by a static field), it will cause the enclosing class to be retained in memory as well.
Inner Classes
Inner classes should generally be static. Non-static inner classes should be used rarely as they always maintain a reference to their parent class. If you store data in a non-static inner class that persists for the entire lifetime (e.g., in a static variable), both the inner class and its parent class will be retained in memory.
Enums
Enums are static by design. Only keep primitive values in enum constructors. For example, storing a resource reference in an enum constructor will cause the resource to be retained in memory, leading to memory leaks. Instead, store the resource path in the enum and use it to get the actual resource when needed.