Received: from PACIFIC-CARRIER-ANNEX.MIT.EDU by po10.MIT.EDU (5.61/4.7) id AA14059; Tue, 25 Jan 00 22:36:00 EST
Received: from hermes.javasoft.com by MIT.EDU with SMTP
	id AA17256; Tue, 25 Jan 00 22:36:58 EST
Received: (from nobody@localhost)
	by hermes.java.sun.com (8.9.3+Sun/8.9.1) id DAA06478;
	Wed, 26 Jan 2000 03:36:35 GMT
Date: Wed, 26 Jan 2000 03:36:35 GMT
Message-Id: <200001260336.DAA06478@hermes.java.sun.com>
X-Authentication-Warning: hermes.java.sun.com: Processed from queue /bulkmail/data/ed_83/mqueue7
X-Mailing: 195
From: JDCTechTips@sun.com
Subject: JDC Tech Tips  January 25, 2000
To: JDCMember@sun.com
Reply-To: JDCTechTips@sun.com
Errors-To: bounced_mail@hermes.java.sun.com
Precedence: junk
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
X-Mailer: Beyond Email 2.2
Content-Transfer-Encoding: quoted-printable
X-Mime-Autoconverted: from 8bit to quoted-printable by hermes.java.sun.com id DAA06478


 J  D  C    T  E  C  H    T  I  P  S

                      TIPS, TECHNIQUES, AND SAMPLE CODE


WELCOME to the Java Developer Connection(sm) (JDC) Tech Tips,=20
January 25, 2000. This issue covers:

         * Using Finally Versus Finalize to Guarantee Quick=20
           Resource Cleanup
         * Using HPROF to Tune Performance         =20
        =20
These tips were developed using Java(tm) 2 SDK, Standard Edition,=20
v 1.2.2, and are not guaranteed to work with other versions.

You can view this issue of the Tech Tips on the Web at=20
http://developer.java.sun.com/developer/TechTips/2000/tt0124.html.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -=20
USING FINALLY VERSUS FINALIZE TO GUARANTEE QUICK RESOURCE CLEANUP


The Java(tm) programming language includes a finalize method that
allows an object to free system resources, in other words, to
clean up after itself. However using finalize doesn't guarantee=20
that a class will clean up resources expediently. A better=20
approach for cleaning up resources involves the finally method=20
and an explicit close statement.=20

To compare the two approaches, let's look at an example.


public class Finalize1 {
        private static final int testIter =3D 100;
               =20
        static public void main(String[] args) {
                int n;
                //Initialize a batch of objects that use finally to clean=
 up
                for (n=3DtestIter; --n>=3D0;) {
                        UsesFinalize uf =3D new UsesFinalize();
                }

                //Initialize a batch of objects that use an explicit clos=
e
                //to clean up.  Note that the code is more complex.  This=
=20
                //is a necessary evil.
                for (n=3DtestIter; --n>=3D0;) {
                        UsesClose uf =3D null;=20
                        try {
                                uf =3D new UsesClose();
                        }
                        finally {
                                if (uf !=3D null)
                                        uf.close();
                        }
                }
                System.out.println("This demo demonstrates the danger of =
relying on finally to expediently close resources.");
                System.out.println("Testing with 100 resources:");
               =20
                //Each of the classes tracking the maximum number of "ope=
n" resources
                //at any given time.           =20
                System.out.println("Using finally to close resources requ=
ired "=20
                                                        + UsesFinalize.ma=
xActive +
                                                        " open resources.=
");  =20
                System.out.println("Using explicit close required "=20
                                                        + UsesClose.maxAc=
tive +
                                                        " open resource."=
);    =20
        }
       =20
        static public class UsesFinalize {
                static int active;
                static int maxActive;
               =20
                UsesFinalize() {
                        active++;
                        maxActive =3D Math.max(active, maxActive);
                }
               =20
                public void finalize() {
                        active--;
                }
        }
       =20
        static public class UsesClose {
                static int active;
                static int maxActive;
               =20
                public UsesClose() {
                        active++;
                        maxActive =3D Math.max(active, maxActive);
                }
               =20
                public void close() {
                        active--;
                }
        }
}

The Finalize1 program takes two alternative approaches to cleaning
up resources. In the first approach it creates 100 objects,
incrementing a counter for each object. It then uses the finalize=20
method to clean up each object. Each time it cleans up an object,=20
it decrements the counter.

In the second approach, 100 objects are also created. However
here the finally statement and an explicit close method are used
to clean up and decrement the counter.

If you run Finalize1, you'll see that the Finalize approach does=20
not decrement the counter. None of the objects are closed. However
the Finally-plus-close approach does the job it's intended to do.
It decrements the counter for each object. It closes all the=20
objects.

The purpose of the Finalize method is often misunderstood by=20
programmers. The Javadoc comment for Finalize states that it's=20
called by the garbage collector on an object when the garbage=20
collector determines that there are no more references to the=20
object. Presumably the garbage collector will, like its civil=20
servant namesake, visit the heap on a regular basis to clean up=20
resources that are no longer in use. =20

As reasonable as it may seem, this interpretation of finalization=20
relies on assumptions about garbage collection that are not
supported by the Java language specification.  The primary purpose=20
of Java garbage collection is not to run finalizers. Garbage=20
collection exists to prevent programmers from calling delete. This=20
is a wonderful feature. For example, if you can't call delete,=20
then you can't accidentally call delete twice on the same object. =20
However, removing delete from the language is not the same thing=20
as automatically cleaning up. The name "garbage collection"=20
promises too much. Confusion might have been saved by using the=20
name "delete prevention" instead. In fact, the Java garbage=20
collection specification imposes only minimal requirements for the=20
behavior of garbage collection, which include:=20

1. Garbage collection might not ever run. If garbage collection=20
   runs at all, and an object is no longer referenced, then that=20
   object's finalize will run. =20
  =20
2. Across multiple objects, finalize order is not predictable.

Either of these rules, taken alone, would be enough to make=20
finalize a risky way to clean up resources. So why do developers=20
leap to the wrong conclusion and rely on finalize? There are two=20
reasons: they are swayed by the analogy to the C++ destructor, and=20
they often get away with it in the short run. Combine a simple=20
project with a better-than-average VM implementation, and finalize=20
will appear almost as reliable as a C++ destructor. Don't be=20
fooled by this temporary good luck. If you rely on finalize, your=20
code will not scale to larger projects, and it will not run=20
consistently on different virtual machines. =20

The correct approach to resource cleanup in Java language programs
does not rely on finalize. Instead, you simply write explicit close=20
methods for objects that wrap native resources. If you take this=20
approach, you must document that the close method exists and when=20
it should be called. Callers of the object must then remember to=20
call close when they are finished with a resource.=20

This probably does not live up to your hopes for garbage=20
collection, since you are back to the manual labor of freeing=20
resources yourself. Moreover, this code still has a problem: if an=20
exception is thrown from somewhere inside the method that calls=20
close, the close method will never be reached. This calls for a way=20
to force a code block to be executed, regardless of exceptions.=20
Java's finally clause fits the bill perfectly. After any try block=20
in Java, you can specify a finally block which will execute, no=20
matter how the try block exits--either normally or exceptionally. =20

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -=20
USING HPROF TO TUNE PERFORMANCE

How fast is the Java(tm) platform? For many applications, the=20
answer is "fast enough"-- if you make careful choices in your=20
design and make good use of the language. But while design=20
documents and coding standards might encourage efficient code,=20
the only way to know for sure is by profiling, that is, obtaining=20
method timing and other information pertinent to performance.=20
Fortunately, the tools that you need to do profiling are part of=20
the Java(tm) 2 SDK. This tip will get you started with HPROF,=20
the Java Profiler Agent, and present an example where a simple code=20
snippet is improved to run 100 times faster. =20

To see HPROF's options, enter the following at a command prompt:=20

java -Xrunhprof:help

One specification you can make for HPROF is cpu=3Dsamples. This=20
setting enables you to profile by sampling. With sampling, the=20
virtual machine regularly pauses execution and checks to see which=20
method call is active. With enough samples (and a decent=20
sampling rate), you can pinpoint where your code spends its time. =20
For example, consider the following example:=20
=20
package com.develop.demos;
=20
import java.io.IOException;

public class TestHprof {
    public static String cat =3D null;
    public final static int loop=3D5000;
   =20
    public static void makeString() {
        cat =3D new String();
        for (int n=3D0; n<loop; n++) {
            addToCat("more");
        }   =20
    }
   =20
    public static void addToCat(String more) {
        cat =3D cat + more;
    }
   =20
    public static void makeStringInline() {
        cat =3D new String();
        for (int n=3D0; n<loop; n++) {
            cat =3D cat + "more";
        }
    }

    public static void makeStringWithLocal() {
        String tmp =3D new String();
        for (int n=3D0; n<loop; n++) {
           tmp =3D tmp + "more";
        }
        cat =3D tmp;
    }
   =20
    public static void makeStringWithBuffer() {
        StringBuffer sb =3D new StringBuffer();
        for (int n=3D0; n<loop; n++) {
            sb.append("more");
        }
        cat =3D sb.toString();
    }
    public static void main(String[] args) {
    	long begin =3D System.currentTimeMillis();
    =09
    	if (null !=3D System.getProperty("WaitForProfiler")) {
	    	System.out.println("Start your profiler, then press any key to begi=
n...");
    		try {
    			System.in.read();
    		}
    		catch (IOException ioe) {
	    	}
    	}
           =20
        makeString();
        makeStringInline();
        makeStringWithLocal();
        makeStringWithBuffer();
       =20
        long end =3D System.currentTimeMillis();
        System.out.println("Total run time of " + (end - begin) + " milli=
seconds");
    }
}=20
=20

A call to makeString simply builds up a long string by repeated=20
concatenation. This is definitely a slow way to build the string,=20
but how can it be made faster?  One possibility is to eliminate the=20
overhead of a function call by putting the addToCat method inline,
as in makeStringInLine.=20
   =20
Another possible optimization is illustrated in makeStringWithLocal.
This method uses a temporary local variable; a local variable might=20
be accessed more quickly than the static cat.=20
   =20
Still another possibility is to use the StringBuffer class=20
instead of String, since the intermediate results don't have=20
to be stored in Strings. This is demonstrated in=20
makeStringWithBuffer.=20
   =20
Which of these four implementations is fastest? Let's run HPROF=20
against the program. Running HPROF will help determine which=20
optimizations are worthwhile.=20

java -Xrunhprof:cpu=3Dsamples,depth=3D6 com.develop.demos.TestHprof

Notice the depth=3D6 specification. This indicates a stack trace=20
depth of 6. Note too that by default the profiler output goes=20
to java.hprof.txt. The interesting part of this file is the table=20
at the bottom which lists the percentage of time spent in each=20
different stack trace:=20

CPU SAMPLES BEGIN (total =3D 7131) Wed Jan 12 13:12:40 2000
rank   self  accum   count trace method
   1 20.57% 20.57%    1467    47 demos/TestHprof.makeStringInline
   2 20.40% 40.98%    1455    39 demos/TestHprof.addToCat
   3 20.28% 61.25%    1446    53 demos/TestHprof.makeStringWithLocal
   4 11.85% 73.10%     845    55 java/lang/String.getChars
   5 11.75% 84.85%     838    42 java/lang/String.getChars
   6 11.72% 96.58%     836    50 java/lang/String.getChars
   (remaining entries less than 1% each, omitted for brevity)

The self column is an estimate of the percentage of time=20
a particular stack trace is active. In this case, you want to=20
time four methods (makeString, makeStringInline,=20
makeStringWithLocal, and makeStringWithBuffer); let's call these=20
top-level methods. You cannot simply add the times for these=20
methods, because a sample that was not in a top-level method=20
might still have that top-level method somewhere in its call stack.=20
So, for each entry in the table, you need to crawl back up the=20
stack to find the associated top-level method. The trace column=20
is a pointer to the needed information, which is higher up in=20
the HPROF output file. For example, the 4th ranked sample is=20
trace 55:=FD=20

TRACE 55:
	java/lang/String.getChars(:Compiled method)
	java/lang/StringBuffer.append(:Compiled method)
	com/develop/demos/TestHprof.makeStringWithLocal \
		(TestHprof.java:Compiled method)
	com/develop/demos/TestHprof.main(TestHprof.java:57)

Ahah! Trace 55 leads back to makeStringWithLocal, so its time=20
should be added to the time for 3rd-ranked Trace 53, which is a=20
direct invocation of makeStringWithLocal. If necessary, you could=20
continue this process, and cross-reference all the call stacks=20
and the CPU samples by hand. Alternately, you could use a tool=20
that interprets the profiling output. In this simple example,=20
the top six samples are enough. Three of the top-level methods=20
(makeString, makeStringInline, and makeStringWithLocal) each have=20
two entries in the top six. Adding up each method's entries leads=20
to a tie. Each of the three contributed over 30% of the total=20
running time. On the other hand, makeStringBuffer's stack traces=20
are way down the list, and total less than 0.3% of the runtime of=20
the application. In other words, putting the function inline and=20
using a local variable didn't help, but switching from String to=20
StringBuffer caused the code to execute over 100 times faster. =20
Vive la HPROF!=20

This example only scratches the surface of Java profiling. =20
Profiling data can often be used not only to time methods, but=20
also to explain why one implementation is faster than another.=20
With HPROF's cpu=3Dtimings flag, you can profile by explicitly=20
timing methods. This gives more accurate results than sampling,=20
but is slower and more intrusive. The heap options can be used to=20
track memory problems.=20

Profiling is an important arrow in any Java developer's quiver,=20
and HPROF is a free way to get started. Whatever tools you choose=20
to use, make sure that you profile your code to find and prove=20
performance gains.

.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .

- NOTE
The names on the JDC mailing list are used for internal Sun
Microsystems(tm) purposes only. To remove your name from the list,
see Subscribe/Unsubscribe below.


- FEEDBACK
Comments?  Send your feedback on the JDC Tech Tips to:

jdc-webmaster@sun.com


- SUBSCRIBE/UNSUBSCRIBE
The JDC Tech Tips are sent to you because you elected to subscribe
when you registered as a JDC member. To unsubscribe from JDC email,
go to the following address and enter the email address you wish to
remove from the mailing list:

http://developer.java.sun.com/unsubscribe.html


To become a JDC member and subscribe to this newsletter go to:

http://java.sun.com/jdc/


- ARCHIVES
You'll find the JDC Tech Tips archives at:

http://developer.java.sun.com/developer/TechTips/index.html

- COPYRIGHT
Copyright 2000 Sun Microsystems, Inc. All rights reserved.
901 San Antonio Road, Palo Alto, California 94303 USA.

This document is protected by copyright.  For more information, see:

http://developer.java.sun.com/developer/copyright.html


This issue of the JDC Tech Tips is written by Stuart Halloway,
a Java specialist at DevelopMentor (http://www.develop.com).

JDC Tech Tips=20
January 25, 2000














