By Jack Shirazi
Cover | Table of Contents | Colophon
System.currentTimeMillis(
) and then subtract this from a later timestamp to
determine the elapsed time. This works well for elapsed time
measurements that are not short. Other types of
measurements have to be system-specific and often
application-specific. You can measure:main( ) to maintest( ), and call maintest( ) twice from a new main( )).System.currentTimeMillis(
)
in the code to get timestamps is the
only reliable way to determine the time taken by each part of the
application. In addition, System.currentTimeMillis(
) is quick and has no effect on application timing (as long
as you are not measuring too many intervals or ridiculously short
intervals; see the discussion in Section 1.7 in Chapter 1).-verbosegc
option with the VM. This option prints
out time and space values for objects reclaimed and space recycled as
the reclamations occur. The printout includes explicit synchronous
calls to the garbage collector (using
System.gc( )) as
well as asynchronous executions of the garbage collector, as occurs
in normal operation when free memory available to the VM gets low.System.gc( ) does not necessarily force a
synchronous garbage collection. Instead, the gc( )
call is really a hint to the runtime that now is a good time to run
the garbage collector. The runtime decides whether to execute the
garbage collection at that time and what type of garbage collection to
run.-verbosegc. The following code fragment creates
lots of objects to force the garbage collector to work, and also
includes some synchronous calls to the garbage collector:package tuning.gc;
public class Test {
public static void main(String[] args)
{
int SIZE = 4000;
StringBuffer s;
java.util.Vector v;
//Create some objects so that the garbage collector
//has something to do
for (int i = 0; i < SIZE; i++)
{
s = new StringBuffer(50);
v = new java.util.Vector(30);
s.append(i).append(i+1).append(i+2).append(i+3);
}
s = null;
v = null;
System.out.println("Starting explicit garbage collection");
long time = System.currentTimeMillis( );
System.gc( );
System.out.println("Garbage collection took " +
(System.currentTimeMillis( )-time) + " millis");
int[] arr = new int[SIZE*10];
//null the variable so that the array can be garbage collected
time = System.currentTimeMillis( );
arr = null;
System.out.println("Starting explicit garbage collection");
System.gc( );
System.out.println("Garbage collection took " +
(System.currentTimeMillis( )-time) + " millis");
}
}foo( ) at the top of
the stack, then the assumption is that method foo(
) takes 10% of the running time. However, this is a
sampling
technique
, and so it is not
foolproof: methods can be missed altogether or have their weighting
misrecorded if some of their execution calls are missed. But usually
only the shortest tests are skewed. Any reasonably long test (i.e.,
over seconds, rather than milliseconds) will normally give correct
results.www.java.sun.com for
"HAT
"), which can analyze
the default profiling mode with Java 2, provides a little more
information from the profiler output, but if you are relying on this,
profiling object creation will require a lot of effort. To use this
tool, you must use the binary output option to the profiling option:java -Xrunhprof:format=b <classname>
java.lang.Object
class to catch most nonarray
object-creation calls. This is not a supported feature, but it does
seem to work on most systems, because all constructors chain up to
the Object class's
constructor, and any explicitly created
nonarray object calls the constructor in Object as
its first execution point after the VM allocates the object on the
heap. Objects that are created implicitly with a call to
clone( )
or by
deserialization do not call the
Object class's constructor, and so are
missed when using this technique.Object class with this
book. But I can show you the simple changes to make to the
java.lang.Object class to track object creation.Object
constructor to pass this to some object-creation
monitor you are using. java.lang.Object does not
have an explicitly defined constructor (it uses the default empty
constructor), so you need to add one to the source and recompile. For
any class other than freeMemory( ) and
totalMemory( ) in the
java.lang.Runtime class.totalMemory( )
returns a long, which is the number of bytes
currently allocated to the runtime system for this particular Java VM
process. Within this memory allocation, the VM manages its objects
and data. Some of this allocated memory is held in reserve for
creating new objects. When the currently allocated memory gets filled
and the garbage collector cannot allocate sufficiently more memory,
the VM requests more memory to be allocated to it from the underlying
system. If the underlying system cannot allocate any further memory,
an OutOfMemoryError error is thrown. Total memory
can go up and down; some Java runtimes can return sections of unused
memory to the underlying system while still running.freeMemory( ) returns a
long, which is the number of bytes available to
the VM to create objects from the section of memory it controls
(i.e., memory already allocated to the runtime by the underlying
system). The free memory increases when a garbage collection
successfully reclaims space used by dead objects, and also increases
when the Java runtime requests more memory from the underlying
operating system. The free memory reduces each time an object is
created, and also when the runtime returns memory to the underlying
system.freeMemory( ) and
totalMemory( ) is straightforward, and I include
here a simple class that does this graphically. It creates three
threads: one to periodically sample the memory, one to maintain a
display of the memory usage graph, and one to run the program you are
monitoring. Figure 2-1 shows a screen shot of the
memory monitor after monitoring a run of the
System.currentTimeMillis( ) to get timestamps
if you need to determine absolute times. Never use the timings
obtained from a profiler as absolute
times.Runtime.totalMemory( ) and
Runtime.freeMemory( )-verbosegc
option. This prints out time and space
values for objects reclaimed and space recycled. The printout
includes explicit synchronous calls to the garbage collector (using
System.gc( )
) as well as asynchronous executions of
the garbage collector, as occurs in normal operation when free memory
available to the VM gets low. You can try to force the VM to execute
only synchronous garbage collections by using the
-verbosegc
option. This prints out time and space
values for objects reclaimed and space recycled. The printout
includes explicit synchronous calls to the garbage collector (using
System.gc( )
) as well as asynchronous executions of
the garbage collector, as occurs in normal operation when free memory
available to the VM gets low. You can try to force the VM to execute
only synchronous garbage collections by using the
-noasyncgc option to the Java executable (no
longer available from JDK 1.2). This option does not actually stop
the garbage-collector thread from executing: it still executes if the
VM runs out of free memory (as opposed to just getting low on
memory). Output from the garbage collector running with
-verbosegc is detailed in Section 2.2.-Xmx
/-mx option). The
garbage collector's space-reclamation algorithm tends to change
with each version of the JDK.StreamTokenizer
(see Section 5.4), are inefficient and can be replaced
quite easily since you normally use them in small, well-defined parts
of a program. Other JDK classes, like
Date
,
BigDecimal
, and
String
are used all over the place, and it can
take a large effort to replace references with your own versions of
these classes. The best way to replace these classes is to start from
the design stage, so that you can consistently use your own versions
throughout the application.java.lang.Math
methods were changed from native to call the corresponding methods in java.lang.StrictMath
. StrictMath provides bitwise consistency across platforms; earlier versions of Math used the platform-specific native functions that were not identical across all platforms. Unfortunately, StrictMath calculations are somewhat slower than the corresponding native functions. My colleague Kirk Pepperdine, who first pointed out the performance problem to me, puts it this way: "I've now got a bitwise-correct but excruciatingly slow program." The potential workarounds to this performance issue are all ugly: using an earlier JDK version, replacing the JDK class with an earlier version, or writing your own class to manage faster alternative floating-point calculations.ints or
booleans produced highly varying and misleading
times.http://www.extreme.indiana.edu/hpjava/) is a
prototype compiler that automatically parallelizes parts of a Java
application to improve performance.int foo = 9*10;
9*10 is evaluated to 90
before compilation is completed. The result is as if the line read:int foo = 90;
String foo = "hi Joe " + (9*10);
String foo = "hi Joe 90";
-verbose
option, but note that this ordering is
fragile: slight changes in the application can easily alter the
loading order of classes. A further extension to this idea is to
include your own
classloader
that opens the ZIP/JAR file itself and reads in all files
sequentially, loading them into memory immediately. Perhaps the final
version of this performance improvement route is to dispense with the
ZIP/JAR filesystem: it is quicker to load the files if they are
concatenated together in one big file, with a header at the start of
the file giving the offsets and names of the contained files. This is
similar to the ZIP filesystem, but it is better if you read the
header in one block, and read in and load the files directly rather
than going through the java.util.zip classes.