Java Memory Management Brett Hodges Applications Engineer Sitraka Europe
[email protected]
Overview • Review of Java’s Memory Model • Loitering Objects • Effective Memory Management – Design – Implementation – Documentation
• Conclusion and Further Reading
J AO O-
© 2001 - Sitraka Inc.
Memory Safety in Java • Memory safety was a key aspect in the design of... – The Java Language • Absence of any form of pointer arithmetic • Can not directly reclaim object memory
– And the Java Virtual Machine (JVM) • Bytecode instruction set • Runtime checks (array bounds, reference casts) • Garbage collection J AO O-
© 2001 - Sitraka Inc.
Memory Safety in Java • Entire classes of memory-related problems were eliminated – Buffer overruns – De-referencing stale pointers – Memory leaks
• However memory management issues remain – Loitering Objects – Object Cycling
• Either of these issues can easily undermine the performance of your application J AO O-
© 2001 - Sitraka Inc.
Java Memory Management • You have to understand the assumptions, and limitations, of Java’s memory management policies in order to use it effectively • Principally this entails understanding the role of object references with respect to garbage collection
J AO O-
© 2001 - Sitraka Inc.
Java Memory Management • As objects are created within a running Java program, they’re stored within the JVM’s heap • Central to Java’s memory management subsystem is the notion of garbage collection – Removes objects that are no longer needed – Impossible to determine, in general, so Java uses an approximation... • Removes objects that are no longer reachable (accessible to the program at the beginning of a garbage collection cycle) J AO O-
– The reachability test starts at the heap’s root set © 2001 - Sitraka Inc.
The Root Set • Set of foundational object references within your application – static reference fields within class definitions – Local reference variables within the method frames of each thread stack
• The contents of the JVM’s root set changes dynamically – As threads enter and exit methods, local reference variables come into and go out of scope (enter and leave the root set) J AO O-
© 2001 - Sitraka Inc.
Dynamic Nature of the Root Set - 1 • Consider a single thread of execution... 1 public 2 class MyApp 3 { 4 static private MyApp myApp = null; 5 6 static public 7 void 8 main( String[] args ) 9 { 10 myApp = new MyApp( ); 11 myApp.method1( ); 12 myApp.method2( ); 13 }
J AO O-
© 2001 - Sitraka Inc.
Root Set: MyApp myApp String[] args
Dynamic Nature of the Root Set - 2 14 15 16 17 18 19 20 21 22 23 24 25 26 27 }
J AO O-
private void method1( ) { FooObject fooObj = new FooObject( ); Root Set: ... FooObject fooObj } MyApp myApp String[]
private void method2( ) { BarObject barObj = new BarObject( ); ... }
© 2001 - Sitraka Inc.
args
Dynamic Nature of the Root Set - 3 1 public 2 class MyApp 3 { 4 static private MyApp myApp = null; 5 6 static public 7 void 8 main( String[] args ) 9 { 10 myApp = new MyApp( ); 11 myApp.method1( ); 12 myApp.method2( ); 13 } J AO O-
© 2001 - Sitraka Inc.
Root Set: MyApp myApp String[] args
Dynamic Nature of the Root Set - 4 14 15 16 17 18 19 20 21 22 23 24 25 26 27 }
J AO O-
private void method1( ) { FooObject fooObj = new FooObject( ); ... } private void method2( ) { BarObject barObj = new BarObject( ); Root Set: ... BarObject barObj } MyApp myApp String[] © 2001 - Sitraka Inc.
args
Dynamic Nature of the Root Set - 5 1 public 2 class MyApp 3 { 4 static private MyApp myApp = null; 5 6 static public 7 void 8 main( String[] args ) 9 { 10 myApp = new MyApp( ); 11 myApp.method1( ); 12 myApp.method2( ); 13 } J AO O-
© 2001 - Sitraka Inc.
Root Set: MyApp myApp String[] args
Reachable Objects • Elements within the root set directly refer to objects within the heap of the JVM • Reference variables within those objects refer to further objects within the Heap (indirectly reachable from the Root Set)
Heap
J AO O-
Root Set © 2001 - Sitraka Inc.
Garbage Collection and Object Reachability • Objects within the heap can be considered to be in one of two “states”: – Allocated • Exists within the JVM’s heap
– Reachable • A path exists (directly or indirectly) from a member of the root set, through a sequence of references, to that object
J AO O-
© 2001 - Sitraka Inc.
Loitering Objects Java’s “Memory Leaks”
What is a “Memory Leak” in Java ? • Memory leaks (as traditionally defined in C/C++) cannot occur in Java – That memory is reclaimed by the Garbage Collector
• However, Java programs can still exhibit the macro-level symptoms of traditional memory leaks – Process size seemingly grows without bounds
• Occurs when objects that have outlived their usefulness to the application remain within the heap through successive garbage collections J AO O-
© 2001 - Sitraka Inc.
What is a “Memory Leak” in Java? • We can extend the set of object states to three: – Allocated • Exists within the JVM’s heap
– Reachable • A path exists (directly or indirectly) from a member of the root set, through a sequence of references, to that object
– Live • From the intent of the application’s design, the program will use the object (meaning at least one of its public fields will be accessed and/or one of its public methods will be invoked) along some future path of execution J AO O-
© 2001 - Sitraka Inc.
What is a “Memory Leak” in Java? Allocated Reachable Live
J AO O-
© 2001 - Sitraka Inc.
Memory leak in C/C++ (handled by JVM’s GC) “Memory leak” in Java
Memory Leaks: C/C++ vs. Java • Memory leak in C/C++ – The object has been allocated, but it’s not reachable • malloc()/new, but forgot to free()/delete before overwriting the pointer to the object
• “Loitering Object” in Java – The object is reachable, but it’s not live • The object has reached the end of its designed lifecycle and should be reclaimed, but an erroneous reference to it prevents the object from being reclaimed by the GC
– Object is reachable to the GC, but the code to fix the leak may not be available to us J AO O-
• e.g. private reference field within an obfuscated class that you don’t have the source code to © 2001 - Sitraka Inc.
“Memory Leaks” in Java • Impact can be very severe – Rarely a single object, but an entire subgraph of objects
• A single lingering reference can have massive memory impact (and a Unintentional reference significant performance impact) – Overall process requires more memory than necessary – JVM’s memory subsystem works harder
J AO O-
– In the worst case, your Java application will throw an OutOfMemoryError and terminate © 2001 - Sitraka Inc.
Lingering Transitional References • A reference to a short-term object is used transiently by a long-term object but the longterm object doesn’t clear the reference when it’s done with it – The reference to the short-term object is always reset on the next use but, in the intervening time, the shortterm object loiters around needlessly
• Not a problem in C/C++ – Benign dangling reference J AO O-
© 2001 - Sitraka Inc.
Example: Singleton Pattern • Class with one instance (but once created that instance will typically exist for the duration of the program) – e.g. An application’s print subsystem printer = Printer.getInstance( ); // Long-term object . . . printer.submit( printJob1 ); // Short-term printJob printer.print( ); printJob1 = null; // Get rid of printJob . . . printer.submit( printJob2 ); // Short-term printJob printer.print( ); printJob2 = null; // Get rid of printJob J AO O-
© 2001 - Sitraka Inc.
Singleton Pattern (continued) 1 public final 2 class Printer 3 { 4 static private Printer singleton = null; 5 private PrintJob fPrintJob = null; 6 7 private 8 Printer( ) { } // Hide the constructor… 9 10 static public Printer 11 getInstance( ) 12 { 13 if( singleton == null ) { 14 singleton = new Printer( ); 15 } 16 return( singleton ); J 17 } AO O-
© 2001 - Sitraka Inc.
Singleton Pattern (continued) 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 } J AO O-
public void submit( PrintJob printJob ) { fPrintJob = printJob; } public void print( ) { // Actually print fPrintJob... ... ... // *Forgot* to set fPrintJob to null... }
The object referred to by fPrintJob will needlessly loiter until the next invocation of submit() © 2001 - Sitraka Inc.
Loitering Objects Recap • Objects that remain within the heap past their useful life to the application – Can be a significant burden on the running process – Arise from erroneous references to objects
• To eliminate loitering objects from your application, you must first: – Identify which objects (if any) are loitering within your application – Determine why those objects remain reachable beyond their designed lifetime J AO O-
© 2001 - Sitraka Inc.
Eliminating Loitering Objects Strategies and Tactics
Reference Management • The key to effective memory management in Java is effective reference management • What undermines effective reference management ? – Lack of awareness of the issue – Bad habits from C/C++ development – Class Libraries and Application Frameworks
J AO O-
• Ill-defined reference management policies • Encapsulate flawed reference assignments • Tool (IDEs and others) generated software © 2001 - Sitraka Inc.
Reference Management: Improving the Software You Create • As a Java programmer, how can I effectively manage references within my software, and promote better reference management ? • Success at three levels 1. Design 2. Implementation 3. Documentation J AO O-
© 2001 - Sitraka Inc.
1. Design for Reference Management • For each application-level use case, explicitly characterize: a. The life cycle of each object b. The interrelationships (nature and duration) between various objects
J AO O-
© 2001 - Sitraka Inc.
1a. Object Lifecycles • For each object required in your design to fulfill an application-level use case, you need to define: – Its point of creation – The duration of its usefulness – The point at which it should be eliminated from the runtime environment J AO O-
© 2001 - Sitraka Inc.
1b. Inter-Object Relationships • Objects establish relationships with one another as they collaborate to accomplish their goals • Examples: – Composition (a has-a relationship) – Association (a uses-a relationship)
• Relationship life cycles J AO O-
– Relationships are established, exist for a defined period of time and are then revoked © 2001 - Sitraka Inc.
1b. Inter-Object Relationships • When designing methods that establish and revoke relationships, think “Symmetry” – If you define a method that establishes a relationship, ensure you define a method that revokes it
• The Observer Pattern subject.addObserver( Observer ) subject.removeObserver( Observer ) J AO O-
© 2001 - Sitraka Inc.
Be Attentive To Relative Object Lifecycles • Loiterers often occur when: – A short life cycle object is passed (via a method call) to a long life cycle object – But the long life cycle object erroneously retains the reference to the short life cycle object longer than necessary
J AO O-
© 2001 - Sitraka Inc.
2. Implementation • Loitering objects often arise from simple coding oversights or omissions – Forgot to null-ify a reference variable – Failure to remove an object from an internal list
• Use heap analysis tools to validate that your implementation adheres to your design J AO O-
© 2001 - Sitraka Inc.
Reference Variable Scope • Three forms of reference variables: – Class-based: • Reference variables within a class definition that have a static attribute associated with them
– Object-based: • Non static reference variables within a class definition
– Method-based: • Reference variables defined within the scope of a method J AO O-
© 2001 - Sitraka Inc.
Reference Variable Scope • Don’t be concerned about assignments to method-based reference variables within methods of short execution time • Be attentive of assignments to classbased and object-based reference variables, and method-based reference variables within methods of long execution times J AO O-
© 2001 - Sitraka Inc.
3. Documentation • When implementing a method that accepts an object reference as an argument and retains it internally, document (in the javadoc for that method argument) the symmetric method you have written that eliminates the retained reference • Design by Contract
J AO O-
– Easier for the clients of the method to avoid the problem of loitering objects – Easier for you to validate your implementation while unit testing © 2001 - Sitraka Inc.
Clearly Document the References You Retain • Continuing with the Observer pattern example... void addObserver( Observer ) Adds the Observer argument to the subject's internal list of observers. To remove the observer from this internal list, invoke the removeObserver( Observer ) method.
J AO O-
© 2001 - Sitraka Inc.
Eliminate Subgraph Elements • If you can not fix the root cause of a loitering object, free up as much of the loitering subgraph as you can – null-ify the loitering object’s internal references so the elements of the subgraph can be reclaimed by the garbage collector
Unintentional reference
J AO O-
Unintentional reference
© 2001 - Sitraka Inc.
Conclusion • The key to effective memory management in Java is effective reference management – Incorporate reference management in your design – Validate your Java implementation with the heap analysis tools within Memory Debugger – Document your reference management policies so others will know what to do J AO O-
© 2001 - Sitraka Inc.
Further Reading • Loitering Objects and Java Framework Design by Leonard Slipp, JavaReport, Volume 6, Number 1 (January 2001) www.javareport.com/html/from_pages/article.asp?id=249&mon=1&yr=2001
• How Do You Plug Java Memory Leaks? by Ethan Henry and Ed Lycklama, Dr. Dobb’s Journal, Java Q&A Column,Volume 25, Number 2 (February 2000) www.ddj.com/articles/2000/0002/0002l/0002l.htm
• Memory Leaks in Java Programs by Joel Nylund, JavaReport, Volume 4, Number 11 (November 1999). www.javareport.com/html/from_pages/oldarticles.asp?ArticleID=165
J AO O-
© 2001 - Sitraka Inc.