Title: JProbe Memory Debugger Tips and Tricks
1JProbe Memory Debugger Tips and Tricks
- Leonard Slipp
- Quest Software
2Goals of Our Session
- Preparing Your Investigation
- Object Cycling Analysis
- What it is and why its important
- Investigative Setup
- Interpreting Runtime Information
- Loitering Object (Memory Leak) Analysis
- What it is and why its important
- Investigative Setup
- Interpreting Runtime Information
- Snapshot Analysis
3Memory 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
4Memory Safety in Java
- Entire classes of memory-related problems were
eliminated - Buffer overruns
- De-referencing stale pointers
- Memory leaks
- However subtle memory management issues remain
that can easily undermine the performance of your
application - Object Cycling
- Loitering Objects (Javas Memory Leaks)
5Preparing Your Investigation
6Tip Adopt a Use Case-centric Investigative
Approach
- As an investigator, you want to be very
comfortable with the users view of your
application - Why Use Case-centric Investigation ?
- Centers investigation on the users experience
and how the symptoms of the underlying flaw
affect them - Focus on the execution path of the use case, and
how the underlying implementation is flawed
7Our Example Application
- A servlet-based J2EE application with a single
use case - Select a company on the applications main page
- Other applications
- Sign on
- Search catalog
- Add to virtual shopping cart
- Go to the virtual check out
- etc..
8Our Example Application (Cont)
- Servlet invocation
- Reads the historical price information from an
XML file - Reformats the data into an HTML table which is
then returned to the browser and rendered
9Tip Ensure Youre Confident with the Underlying
Architecture
- You need to have a comfortable literacy with the
internal architecture and design goals of the
application - Names of all the packages
- Names of key classes and methods
- For each of the application use cases, ensure you
are familiar with - The principle methods invoked along that
execution path - Any attempts at optimizations (caching,
pooling, etc -) - If youre not familiar with the underlying design
and its implementation, the investigation will be
much more difficult
10Tip Ensure Youre Confident with the Underlying
Architecture
- In our example program
- One Package
- com.acme.
- Four Principle Classes/Interfaces
- com.acme.StockServlet
- com.acme.StockDataJDOM
- com.acme.PageCache
- com.acme.CachedPage
- The program uses a Most Recently Used (MRU)
caching algorithm that retains the last 3 most
recently requested pages
11Tip Organize Your Workspace for Fluid
Investigation
- Tile the UI of your application along with the
UI of JProbe so everything is visible - Eliminate the effort of constantly repositioning
and resizing dialog boxes - Focus your attention on the investigation, not on
repositioning dialogs
JProbe UI
Application UI
12Object Cycling Analysis
13Object Cycling
- One of the principal causes of performance loss
in Java is the excessive creation of short life
cycle objects - Performance loss is due to...
- Memory allocation within the JVM heap
- Object initialization via chain of constructor
calls - Enhanced garbage collection activity
- As a performance investigator, identify those
methods in your application-level that are object
cycling - They are your first candidates to examine for
refactoring to improve performance
14Object Cycling AnalysisInvestigative Setup
- Within the LaunchPad
- Enable Run Garbage Monitor During Use Case
- Enter an asterisk () in the Filter on Package,
Class or Method column of the first line in the
Filter table - You want to see the entire heap contents
- Then press
15Object Cycling AnalysisRuntime Information
- Within the Runtime Heap Summary, before you
select Start Use Case - Select the Garbage Monitor tab, and
- Enter the fully qualified set of methods
package.class.method to track allocations from - com.acme..
package
class
methods
16Object Cycling AnalysisRuntime Information
- Within the Runtime Heap Summary, press Start Use
Case - Then, within your applications UI, begin the use
case
- In this case, well simply select a stock from
the list
17Object Cycling AnalysisRuntime Information
- As GCs within the JVM occur, Garbage Monitor will
indicate - The type of objects reclaimed
- The methods that they were allocated from
- When the use case completes, press Finish Use Case
e
18Object Cycling AnalysisRuntime Information
- When Finish Use Case is pressed, Garbage Monitor
provides a final update to its table
- Examine the top couple of lines in the table
- The doGet() method in com.acme.StockServlet
object cycles through 2,298 StringBuffers just to
service 1 request !
19Loitering Object Analysis(Memory Leaks)
20What 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 - Heap size seemingly grows without bounds
- Occurs when objects that have outlived their
usefulness to the application remain within the
heap through successive garbage collections
21JVM Runtime Data Areas
Heap
- Heap
- The common memory pool where all objects and
arrays are stored - Thread Stack(s)
- One stack per thread of execution
- Each stack consists of a series of method frames
(one per called method) which contain the method
arguments and return value, the local variables
within the method and a bytecode operand stack
for intermediate results - Method Area
- Maintains the data structures for each loaded
class in the JVM
Thread Stack
...
Thread Stack
JVM
Method Area
(Java program) executing within the JVM
(Java program and JVM) executing within the OS
22Java Memory Management
- As objects are created within a running Java
program, theyre stored within the JVMs heap - Central to Javas memory management subsystem is
the notion of garbage collection - Removes objects that are no longer needed
- Undecidable 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) - The reachability test starts at the heaps root
set
23The 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 JVMs 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)
24Dynamic Nature of the Root Set - 1
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
Root Set MyApp myApp String args
25Dynamic Nature of the Root Set - 2
- 14 private void
- 15 method1( )
- 16
- 17 FooObject fooObj new FooObject( )
- 18 ...
- 19
- 20
- 21 private void
- 22 method2( )
- 23
- 24 BarObject barObj new BarObject( )
- 25 ...
- 26
- 27
Root Set MyApp myApp String args
FooObject fooObj
26Dynamic 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
Root Set MyApp myApp String args
27Reachable 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)
28Reachable Objects GC
- At the beginning of a GC cycle, objects within
the heap can be considered to be in one of two
progressive states - Allocated
- Exists within the JVMs heap
- Reachable
- A path exists (directly or indirectly) from a
member of the root set, through a sequence of
references, to that object
29Reachable Objects GC
Allocated
Reachable
30What is a Memory Leak in Java?
- Lets extend the set of object states to three
- Allocated
- Exists within the JVMs 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 applications 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
31What is a Memory Leak in Java?
Allocated
Reachable
Live
32Loitering Objects
- The term Memory Leak has a lot of historical
context from C/C and it doesnt accurately
describe the problem as it pertains to Java - New term Loitering Object or Loiterer
- An object that remains within the Heap past its
useful life to the application - Arise from an invalid reference that makes the
object reachable to the GC
33Loitering Objects
- Impact can be very severe
- Rarely a single object, but an entire sub-graph
of objects - A single lingering reference can have massive
memory impact (and a significant performance
impact) - Overall process requires more memory than
necessary - JVMs memory subsystem works harder
- In the worst case, your Java application will
throw an OutOfMemoryError and terminate
Unintentional reference
34Reference 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
- Ill-defined reference management policies
- Encapsulate flawed reference assignments
- Tool (IDEs and others) generated software
35Heap Investigation Strategy
- You need a structured and disciplined approach to
identify loitering objects systematically - Ensure that the underlying implementation adheres
to the intent of your design - Based on classic scientific testing
- Establish a hypothesis (what you expect to see)
- Design and run an experiment to prove your
hypothesis - Compare the experimental results against your
hypothesis - Resolve the differences (if any)
36Structure Your Hypothesis and Experiments Around
Use Cases
- Focus on the Use Case-centric approach
- Your hypothesis consists of the set of objects
you expect to persist at the end of your use case
(which you know from your architectural design) - Use Memory Debuggers Start Use Case and Finish
Use Case features to define your use case
37Loitering Object AnalysisInvestigative Setup
- Within the LaunchPad
- Disable Run Garbage Monitor During Use Case
- Enter an asterisk () in the Filter on Package,
Class or Method column of the first line in the
Filter table - You want to see the entire heap contents
- Then press
38Loitering Object AnalysisRuntime Information
- Within the Runtime Heap Summary
- The Instance Summary table (below the Heap Graph)
provides a detailed breakdown of the Heap
contents at the leading edge of the Heap Graph
39Loitering Object AnalysisRuntime Information
- When you press Start Use Case
- Two additional columns appear within the Instance
Summary - Count Change
- Memory Change
- Use this information to gauge the net increate in
objects as you exercise your use case
40Loitering Object AnalysisRuntime Information
- As you are testing your Use Case, filter the
table to the classes within your implementation - Enter the fully qualified set of classes
- com.acme.
41Loitering Object AnalysisRuntime Information
- The design intent of our application is that the
3 MRU stock are cached (any older ones are
disposed of) - Load 6 in sequence
- Apple
- Compaq
- Dell
- Gateway
- Hewlett-Packard
- IBM
- Oh, No ! All 6 are being cached (the oldest 3
are loitering)
42Resolving Loitering Object Flaws
- Loitering Objects represent either a design flaw
or an implementation flaw within the application - How should applications be designed and
implemented to avoid these flaws ? - Knowing how they can be avoided helps us
investigate how they they arose in our application
43Design for Reference Management
- For each application-level use case, explicitly
characterize - a. The life cycle of each object
- b. The inter-relationships (nature and duration)
between various objects
44Object 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
45Object Lifecycles
- In Java, creating an object within the runtime
environment is an explicit act, while its
elimination is an implicit one - Defining within your design the point when
your object should be eliminated will help you
validate the correctness of your subsequent Java
implementation
46Inter-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
- Relationships are established, exist for a
defined period of time and then are revoked - Relationships are based on Java references
47Inter-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 )
48Implementation
- 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
- Difficult to detect, except in catastrophic
situations - The Java Runtime Environment doesnt provide any
insight
49Reference 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
50Reference Variable Scope
- Dont be concerned about assignments to
method-based reference variables within methods
of short execution time - Be attentive of assignments to class-based and
object-based reference variables, and
method-based reference variables within methods
of long execution times
51Loitering Object AnalysisSnapshot Analysis
- When your press Finish Use Case, JProbe will take
a Heap snapshot - Entire contents of the Heap at that time
- Load the snapshot into the Heap Browser
- View identical to the Instance Summary
- S
- Select the Class of loitering objects, and open
the Instance Detail dialog
52Loitering Object AnalysisSnapshot Analysis
- The Instance Detail dialog
- Select one of the oldest loitering objects
- Travers the references within the heap determine
which references are holding on to the loitering
object
s
g
53Finding the Culprit
- Given that we know
- The loitering object, and
- That the flaw is either
- Design-based (object or relationship), or
- Implementation-based
- Carefully explore the references leading to the
loitering object - In our case, our MRU caching algorithm is failing
- It adds to the cache, but it doesnt remove form
it - Turns out to be a misunderstanding on the correct
use of java.util.HashMap
54Loitering Object AnalysisSnapshot Analysis
- The Snapshot Difference dialog
- Used to confirm that your fix indeed solved the
problem (and quantifies the memory saved) - Select the heap snapshot with the loitering
object as the baseline - Compare it to a Heap snapshot taken with the fix
in place
55Session Wrap-up
- Memory management within Java is great
technology, but dont let it lull you into a
false sense of complacency - Adopt a Use Case-centric Investigation Strategy
- Know Your Application
- How its put together and its design intent
- Integrate memory analysis as part of your ongoing
development activity - Dont leave it until youre about to deploy to
production
56Questions ?
- leonard.slipp_at_quest.com