Sie sind auf Seite 1von 14

W H I T E P A P E R Temporary objectsManaging the

Java garbage collector for application


quality and performance

Memory mismanagement can degrade the performance, hamper the

scalability and weaken the robustness of your applications. Learn how to

drill into your programs use of temporary objects to identify problems and

improve code quality.

Automatic memory management is considered one of Javas strong points.


The Java garbage collector (GC) automates object and memory management,

preventing the memory leaks that you see in C++. But if you dont understand

the inner workings of the GC, you could mismanage memory.

A critical element of application correctness, performance and scalability is

object utilization and its effect on memory. Thorough understanding of how

the Java compilers and Java runtime environments use memory would be

very helpful. Yet today, few engineers have expertise or experience in

object optimization.

One area often overlooked in memory utilization of Java applications is


temporary objects. Often misunderstood and rarely seen as a specific memory
problem, temporary objects can reduce the application performance and
increase the size of the memory footprint.

CompuwareCorporation
Temporary objects
What, exactly, are temporary objects? These are objects that are not
persistent, are generally used from very short to medium lengths of time,
and are typically used for short or intermediate storage within the application
at runtime. The Java compiler can also create temporary objects as part of
compiler optimization.

For example, each time your application works with objects, the Java
compilers could potentially perform some optimization. The compiler can use
temporary memory space to manipulate and complete Java statements. These
objects are created and released immediately by the Java compiler on behalf
of the application without having to be explicitly defined by the engineer.

Your application can result in the creation of hundreds or thousands of


temporary objects. Java memory management must run on behalf of your
application for many operations that affect an object, even a temporary one.
The more objects your application utilizes, the more often the garbage
collector must run. And each time the GC has to run, each object and its
related object graph must be managed and optimized. (The object graph
represents the correlated information of object details; well examine this more
closely later.) Object allocation and management tax the CPU and can result
in a larger memory footprint. Application threads may have to wait for the
GC to finish its managing of objects and memory.

Here is an example of source code that results in the use of temporary


memorythe often-used String concatenation example:

java.lang.String:

String r = new String ();

For (int I-0< limit; I+=1) {

r = r + compute(i);

return r;

For all iterations, a new String r is allocated as a temporary object and the
current r is copied into that new instance, after which compute(i) is
added to the new r string. This is done each time through the above loop.
Line 3 in this example would get flagged as a cause of excessive temporary
objects. Heres how to resolve this simple problem:

2
java.lang.StringBuffer:

StringBuffer r = new StringBuffer();

For (int i=0; i< limit; i+=1) {

r.append(compute(i));

return r.toString();

Changing String to StringBuffer has become a common technique


to reduce the number of temporary objects used for string concatenation.
This will result in faster and more scalable software.

Java memory basics


So lets take a closer look at the standard JVM garbage collection of its
memory heap. First, understand that the JVM allocates memory to store all
objects created by all executing Java programs within that JVM; this object
store is called the JVM heap. Javas new operator creates objects and memory
for allocation in the JVM heap at runtime. This differs from C/C++, where
memory is allocated in each applications thread memory stack and heap.

The garbage collector, which manages this shared JVM heap, automatically
monitors objects and frees those that are no longer referenced by the
program. The garbage collector also has house-cleaning duties to fight heap
fragmentation that occurs as a natural effect of creation and destruction of
objects with different sizes during the course of a running program.

Your application architecture and design can introduce different approaches


that could improve application object use. You could consider these areas:
object creation and life cycle, caching, setting initial object size, and
intelligent use of method calls. For further information on these approaches,
see Steve Wilsons book, Java Platform Performance (Sun Press, 2001).

So many objects, so little time


Eventually, youll need to analyze memory use because of several issues that
can arise: The memory footprint (running memory needs) is too large, objects
are not being destroyed or object use is inefficient. Youll need to collect,
correlate and organize many details to get a basic understanding of object
use by the application and within the Java runtime environment.

First, you need a historical understanding of each and every object.


Correlating all information regarding all objects is critical to understanding
what occurred at runtime. Each object has a history: Which class did the
object come from? What method and thread created the object? Each active
3
object has references still being made to it that prevent the garbage collector
from freeing it from the heap. Each object might also have relationships to
other objects. You might find it difficult to track down relationships and
understand how these objects are handled and ultimately freed at runtime.

With simple programs or enough time and skill, you can identify object use
and its effect on the garbage collector to create better-performing applications
that require less memory at runtime. You must map all object graphs over the
applications life or even for a short moment in the applications runtime.
This task is slow and difficult.

Where to start analysis


Analyzing temporary objects for their performance impact and making their
use more efficient is tricky in that there are no direct measurements in Java.
The capabilities available for Java are not very useful for the application
developer. There are APIs such as the Java Virtual Machine Profile Interface
(JVMPI) and the production monitoring application interfaces available in
many application servers today. But these interfaces require writing your own
performance tools, and offer only raw data that still does not provide reference
points for the engineer to solve the problems.

The application engineer must know which entry point, class and method
has created each object and must determine the object size. Additionally,
the engineer needs to know which entry points, methods, threads and other
objects still hold references to each and every object in memory now and in
the past. The correlated information of object details is called the object
graph. The object graph is the map of how objects are related, where
references are held and for how long.

Getting the object graph with the size and number of object instances would
provide most of the information necessary to resolve any problems with object
use efficiency.

You could use these methods to determine the approximate size of an object
by setting up an isolated environment test:

1. Programmatically request garbage collection to get the heap into a


known state.

2. Measure the free heap space with the Runtime.freeSpace method.

3. Create an instance of the class you want to measure and hold a reference
to it.

4. Request garbage collection again.

4
5. Measure the free heap space once again with the Runtime.freeSpace
method.

6. The difference between the first measurement and the second is the size
of the object in question.

This would work well if you have a small application with a limited scope
to analyze. However, there are some problems that give rise to less-than-
accurate results:

Garbage collection execution is not instantaneous and cannot truly be


forced at the actual time requested.
If your test environment or real application uses any caching approach
for application objects, the results could be significantly misleading.
The Runtime.freeSpace method is not dependable for accuracy
down to the byte level.
Temporary objects and indirect effects on the heap may be difficult to
isolate and understand.
The application developer cannot run the Runtime.freeSpace method
against temporary objects to get the object sizes, as there is no reference
available to call the Runtime.freeSpace method.
The volume of information to be correlated for analysis becomes very
large, quickly.

Okay, this is too hardnow what?


Temporary objects are difficult to understand. One approach to grasping the
true impact of temporary objects is to search your code and the output from
the Java compiler for possible locations that leverage temporary objects.
But this approach does not provide insight into the frequency and size of
temporary object usage. These are what lead to possible bottlenecks and
increased size of the application RAM footprint.

An alternative to searching your code manually for all possible references is to


use a software tool that creates the object graph, lets you see the memory used
by objects and gives you a temporary-object history. Determining how many
objects are created and how long they live before being garbage-collected puts
you in the best position to optimize the applications memory use and the
performance impact. Even with the selection of tools available today, object
or memory analysis can be tedious and difficult to understand. Such a tool
must make sense out of all the collected and correlated data. The tool must
then present the information with references to where in the application to
start looking for a solution.

5
Expert help for all usersDevPartner
Most commercial or open-source Java tools for memory analysis typically are
for use by more senior Java engineers. DevPartner Java Edition differs in
that it helps engineers of all skill levels to be more productive sooner. It helps
you analyze temporary object use and assists in resolving issues with
application efficiency.

Quickly attaining application reliability, scalability and performance has been


the focus of the award-winning DevPartner product line from Compuware.
With more than 40,000 users, DevPartner claims a market-leading position
for the distributed application market that encompasses both Microsoft and
Java developers. SD Times in its annual SD Times Top 100 special report,
http://sdtimes.com/news/079/special1.htm, states that DevPartner is the
must-have tool for developers. SD Times identified Compuware as the top
vendor in the Test & Debug category: from the ubiquitous BoundsChecker to
the DevPartner family, its a commanding presence in code testing/debugging.

DevPartner Java Edition (DPJ) successfully delivers on this tradition to


help developers identify and resolve issues. It helps you understand, find and
fix temporary objects through object graphs and inline help. You can turn off
inline help when you become more adept at solving memory problems, rather
than work with a whole different UI as you must do with competing products.

Figure 1. DPJ memory analysis.

6
With DevPartner Java Edition, you can view memory analysis in real time
as in Figure 1. Viewing frequent spikes would suggest many temporary, short-
lived objects and an area for possible improvements in efficiency. DPJ does
not just present raw collected memory analysis data, but presents what the
information means and helps isolate problems faster.

DPJs memory analysis window quickly displays descriptions for each and every
view, with an explanation of the concepts being presented and suggestions of
what to pursue next. Highlighted areas indicate the inline help (in the left-
hand column), which is customized for each user through DPJ preferences.
The ability to turn inline help off and on is helpful to junior engineers, or
those engineers simply wanting a quick refresher on the details of how to
solve performance and memory issues.

Figure 2. DPJ live analysis of temporary objects.

The window in Figure 2 shows the choices available and quick explanations
to help the user focus on problem areas. Notice the Run Garbage Collection
button and the corresponding heap drop in the graph after clicking on that
button. Also notice the Clear Collected Data button in the upper-left
corner. This clears all the statistics collected up until this point and provides
a view of only recent temporary objects. Both features clear out data not
relevant to your problem analysis, reducing the amount of information you
need to analyze.

7
Whats not relevant? The application server startup time and application
initialization time, for example. You need to get to a steady run state before
getting the memory analysis details that are not related to the average runtime
and scalability impacts.

Figure 3. DPJ temporary objects results summary.

Selecting View Temporary Objects... from the upper-left corner (see Figure
3) produces a second browser window with the results of the then-current
statistics of temporary object use. Notice again that the inline help is available
to explain what is being viewed, and the user does not have to go through
separate help screens or to a different path of screens.

When your code executes in a container, or has a lot of calls into it from
system code (such as Swing code), DPJ provides views that start from these
entry points into your code as well as all the underlying Java runtime
methods. This makes it easier to analyze separate paths in your code. This is
not available from any DPJ competitor, and it proves helpful when there are
multiple entry points for items like JSPs, running different methods from your
threads, applets and servlets.

8
Figure 4. DPJ detail popup.

Hovering over or selecting any element of the graph, entry point name or
method name provides more information on that specific item. Figure 4
shows the entry point information to a small application created to highlight
temporary objects for this papers example. From here, the user can go directly
to the call graph or view the source for further analysis.

Object allocations done by your code can be categorized based on how long
it takes for the objects to be collected. The three categories are short-lived,
medium-lived and long-lived. Because long-lived objects are rarely a problem,
a temporary object categorythe sum of the short-lived and medium-lived
object allocationsalso is provided.

Short-lived objects are almost free in terms of their impact on garbage


collection, although there is still a performance penalty for calling the
objects constructor.

Medium-lived objects cause the garbage collector to work harder than


necessary. They consume CPU cycles in such a way that typical performance
profilers have difficulty identifying the particular method causing the problem.
These objects can cause your program to fail with an OutOfMemoryError
under heavy-load situations.

9
Long-lived objects are in use for a long time, such as user interface widgets or
application scope JSP beans. Generally, these would be objects that live until
the application or the Java runtime environment exits. Although these can
be problems for memory footprint and scalability, they are not temporary
objects or part of this example.

DevPartner Java Edition allows you to drill into your programs use of
temporary objects to identify problems and improve the overall quality of
your code. For an in-depth description of temporary objects and their impact
on memory usage and performance, refer to Chapter 7 of Java Platform
Performance by Steve Wilson (Sun Press): http://java.sun.com/docs/books/
performance/1st_edition/html/JPMutability.fm.html#19273.

So what does DPJ provide? It gives you the ability to see which temporary
objects came and went immediately (short-lived), which objects hung around
for a bit longer (medium-lived) and which objects hung around for the life of
the method or application (long-lived).

Look at your application profile with DPJ. If DPJ identifies temporary objects
that negatively impact application performance, there are several possible
solutions depending upon what types of temporary objects and usage are
identified. Java compiler optimization can cause the creation of temporary
objects, which can be explicitly defined and de-referenced within the
application source code. Thus, the same explicitly defined objects can be
reused multiple times without the constant overhead of calling the new
constructor. Reviewing your design may show whether you can reduce
frequent use of short-lived objects by reusing the same objects for multipurpose
workspace objects, therefore reducing the creation and de-referencing of
similar temporary objects multiple times. So, lets go back to a sample
application and solve a problem with DPJ.

Figure 5 shows an example where the primary entry point contains numerous
temporary objects in a short portion of Java code, which is using arrays of
different types.

10
Figure 5. DPJ temporary object view.

Select the call graph view on the LinkedListOperations.populate


class to bring up the view as in Figure 6. This view provides a call graph view
of temporary object use for that method. From this call graph, the user can
quickly analyze the high- and low-level details without leaving this screen.

Figure 6. Call graph view of temporary object use.

11
Selecting the first node in the call graph takes the user to a source view (see
Figure 7) that shows the same information being set using a simple array, array
list and linked list. The difference in usage of the temporary objects using
different program techniques is dramatic, and the impact on performance is
noticeable. DPJ helps users, from new to senior, experienced Java engineers,
isolate and solve problems with code efficiency issues.

Figure 7. DPJ source view of temporary object.

The user can select the doLinkedList node as in Figure 7 on the call
graph or from the source view to get to the called method that is ultimately
the cause of the heaviest temporary object use. Within the doLinkedList
method, the offending line is found (see Figure 8). In this situation, the Java
compiler optimized the use of linked list arrays by creating and copying
temporary objects many times per line execution and within the loop. You
must either explicitly manage temporary workspace objects for the linked list
array to reduce temporary objects, use a different array type or find another
approach to your object hierarchy besides arrays. Within Java, arrays have
limited benefits for the engineer, but there are reasons to use them. Just be
aware of the impacts and tradeoffs to know how to overcome any drawbacks
as shown in this example.

12
Figure 8. Looking into the linked list array method.

DPJ specifies the number of times the worst-offending line of code was
executed, how many objects it used and how many bytes it used. Getting this
level of information and getting it accurate enough to solve problems is not
viable with manual effort or simple tools like those built into the IDE. DPJ
shows the scope of the problem and helps prioritize your best opportunity for
improvements with temporary object use.

By using DPJs performance profiling capabilities, the user can assess the
performance impact of the specific lines of code using the excessive temporary
objects. An engineer can then make modifications and check the new
performance behaviors. Remaining temporary objects are not a matter of
memory leaks, but a matter of RAM footprint and performance impact.
Engineers cannot completely avoid the use of temporary objects, but they
can make the use of temporary objects more efficient.

More efficient solutions with DevPartner Java Edition


DevPartner Java Edition provides the ability to analyze any solution using
the approach that best suits your needs. DPJ enables you to view problems at
a high level and drill down from top to bottom to isolate the problem quickly.
Each user can go to the heart of the source code or call graph to investigate
problem areas without concern that the debugging tool will impede progress.
Less experienced engineers can get a more tutorial-style, step-by-step approach
to problem solving.

13
The net result is that DevPartner Java Edition shifts the focus to fast
Compuware products business solutions and away from time spent on optimizing application code
and professional services and resolving memory or performance bugs.
delivering quality
applications DPJ integrates into JBuilder, WSAD, JDeveloper and OptimalJ as a solution
Compuware is a leading global
to everyday tasks for the developer. DPJs integration into the leading IDEs
provider of software products and
provides immediate access to its features.
professional services which IT
DevPartner Java Edition is also available from the command line and
organizations use to develop,
within an ANT build environment for automation of test results for
integrate, test and manage the
performance, memory and code coverage. The DPJ Server provides
performance of the applications
deployment and server testing for one or multiple tiers. DPJ is the only
that drive their businesses. Our
solution in the market that provides an end-to-end view of all tiers, across all
software products help optimize
machines, all at the same time. You no longer have to shut down the profiling
every step in the application life
tool to reconfigure for a different machine collector. DPJs ability to provide
cyclefrom defining requirements
views of all an applications tiers simultaneously takes the guesswork and extra
to supporting production service
effort out of looking for a needle in a performance haystack; all summary and
levelsfor web, distributed and
detailed information is available for immediate inspection and further analysis.
mainframe platforms. Our services
No other tool can profile and view Java applications the way in which these
professionals work at customer
applications will be deployed: on multiple tiers and multiple machines.
sites around the world, sharing
their real-world perspective and
experience to deliver an
integrated, reliable solution.

Please contact us to learn more


about how our comprehensive
solutions can help your References on Java garbage collection:
organization improve productivity,
create higher quality applications
Arnold, Ken, and James Gosling. The Java Programming Language, Second
and ensure performance in
Edition, Addison-Wesley, Reading, MA, 1998.
production.
Gosling, James, Bill Joy, and Guy Steele. The Java Language Specification,
Second Edition, Addison-Wesley, Reading, MA, 2000.

Jones, Richard, and Rafael Lins. Garbage Collection: Algorithms for Automatic
Dynamic Memory Management, John Wiley & Sons, New York, 1996.

All Compuware products and services listed within are


Lindholm, Tim and Frank Yellin. The Java Virtual Machine Specification, Second
trademarks or registered trademarks of Compuware Edition, Addison-Wesley, Reading, MA, 1999.
Corporation. Java and all Java-based marks are trademarks
or registered trademarks of Sun Microsystems, Inc. in the
United States and other countries. All other company or
product names are trademarks of their respective owners. References on Java performance:
2003 Compuware Corporation
Wilson, Steve. Java Platform Performance, Sun Press, 2001.

To learn more about DevPartner products, visit us at:


www.compuware.com/devpartner

8/03