Garbage Collection Profiling Tools

- 23 - to starting for the first time on a system that has been running for a while, and these both give different timings compared to an application that has been run several times previously on the system. All these variations need to be considered, and a consistent test scenario used. Typically, you need to manage the caches in the application, perhaps explicitly emptying or filling them, for each test run to get repeatable results. The other caches are difficult to manipulate, and you should try to approximate the targeted running environment as closely as possible, rather than test each possible variation in the environment.

2.2 Garbage Collection

The Java runtime system normally includes a garbage collector. [1] Some of the commercial profilers provide statistics showing what the garbage collector is doing. You can also use the -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. [1] Some embedded runtimes do not include a garbage collector. All objects may have to fit into memory without any garbage collection for these runtimes. 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. It is worth looking at some output from running with -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 mainString[] 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 StringBuffer50; v = new java.util.Vector30; s.appendi.appendi+1.appendi+2.appendi+3; } s = null; v = null; System.out.printlnStarting explicit garbage collection; long time = System.currentTimeMillis ; System.gc ; System.out.printlnGarbage collection took + System.currentTimeMillis -time + millis; int[] arr = new int[SIZE10]; null the variable so that the array can be garbage collected time = System.currentTimeMillis ; arr = null; System.out.printlnStarting explicit garbage collection; System.gc ; System.out.printlnGarbage collection took + - 24 - System.currentTimeMillis -time + millis; } } When this code is run in Sun JDK 1.2 with the -verbosegc option, [2] you get: [2] Note that -verbosegc can also work with applets by using java -verbosegc sun.applet.AppletViewer URL. GC: need to expand mark bits to cover 16384 bytes GC: managing allocation failure: need 1032 bytes, type=1, action=1 GC: 0 milliseconds since last GC GC: freed 18578 objects, 658392 bytes in 26 ms, 78 free 658872838856 GC: initscan: 1 ms, scan handles: 12 ms, sweep: 13 ms, compact: 0 ms GC: 0 register-marked objects, 1 stack-marked objects GC: 1 register-marked handles, 31 stack-marked handles GC: refs: soft 0 age = 32, weak 0, final 2, phantom 0 GC: managing allocation failure: need 1032 bytes, type=1, action=1 GC: 180 milliseconds since last GC GC: compactHeap took 15 ms, swap time = 4 ms, blocks_moved=18838 GC: 0 explicitly pinned objects, 2 conservatively pinned objects GC: last free block at 0x01A0889C of length 1888 GC: last free block is at end GC: freed 18822 objects, 627504 bytes in 50 ms, 78 free 658920838856 GC: initscan: 2 ms, scan handles: 11 ms, sweep: 16 ms, compact: 21 ms GC: 0 register-marked objects, 2 stack-marked objects GC: 0 register-marked handles, 33 stack-marked handles GC: refs: soft 0 age = 32, weak 0, final 0, phantom 0 Starting explicit garbage collection GC: compactHeap took 9 ms, swap time = 5 ms, blocks_moved=13453 GC: 0 explicitly pinned objects, 5 conservatively pinned objects GC: last free block at 0x019D5534 of length 211656 GC: last free block is at end GC: freed 13443 objects, 447752 bytes in 40 ms, 78 free 657752838856 GC: initscan: 1 ms, scan handles: 12 ms, sweep: 12 ms, compact: 15 ms GC: 0 register-marked objects, 6 stack-marked objects GC: 0 register-marked handles, 111 stack-marked handles GC: refs: soft 0 age = 32, weak 0, final 0, phantom 0 Garbage collection took 151 millis ... The actual details of the output are not standardized and likely to change between different VM versions as well as between VMs from different vendors. As a comparison, this is the output from the later garbage collector version using Sun JDK 1.3: [GC 511K-96K1984K, 0.0281726 secs] [GC 608K-97K1984K, 0.0149952 secs] [GC 609K-97K1984K, 0.0071464 secs] [GC 609K-97K1984K, 0.0093515 secs] [GC 609K-97K1984K, 0.0060427 secs] Starting explicit garbage collection [Full GC 228K-96K1984K, 0.0899268 secs] Garbage collection took 170 millis Starting explicit garbage collection [Full GC 253K-96K1984K, 0.0884710 secs] Garbage collection took 180 millis As you can see, each time the garbage collector kicks in, it produces a report of its activities. Any one garbage collection reports on the times taken by the various parts of the garbage collector and specifies what the garbage collector is doing. Note that the internal times reported by the garbage collector are not the full time taken for the whole activity. In the examples, you can see the full time - 25 - for one of the synchronous garbage collections, which is wrapped by print statements from the code fragment i.e., those lines not starting with a or [ sign. However, these times include the times taken to output the printed statements from the garbage collector and are therefore higher times than those for the garbage collection alone. To see the pure synchronous garbage collection times for this code fragment, you need to run the program without the -verbosegc option. In the previous examples, the garbage collector kicks in either because it has been called by the code fragment or because creating an object from the code fragment or the runtime initialization encounters a lack of free memory from which to allocate space for that object: this is normally reported as managing allocation failure. Some garbage-collector versions appear to execute their garbage collections faster than others. But be aware that this time difference may be an artifact: it can be caused by the different number of printed statements when using the -verbosegc option. When run without the -verbosegc option, the times may be similar. The garbage collector from JDK 1.2 executes a more complex scavenging algorithm than earlier JDK versions to smooth out the effects of garbage collection running in the background. The garbage-collection algorithm is discussed briefly in Chapter 3 . It cannot be tuned directly, but garbage-collection statistics can give you important information about objects being reclaimed, which helps you tune your application. From JDK 1.2, the VM also handles many types of references that never existed in VM versions before 1.2. Overall, Java 2 applications do seem to have faster object recycling in application contexts than previous JDK versions. It is occasionally worthwhile to run your application using the -verbosegc option to see how often the garbage collector kicks in. At the same time, you should use all logging and tracing options available with your application, so that the output from the garbage collector is set in the context of your application activities. It would be nice to have a consistent way to summarize the information generated with this verbose option, but the output depends on both the application and the VM, and I have not found a consistent way of producing summary information.

2.3 Method Calls