Several attributes of the Java language make performance tuning a bit more of a challenge than typical procedural languages such as RPG or COBOL. None of these attributes are showstoppers. The Java language has seen significant performance improvements on the AS/400 and other platforms since it was introduced just a few years ago. It is fair to state, however, that tuning Java for performance is more important than tuning RPG or COBOL. One reason for this is the extensive nature of the Java runtime. Java 2 supports over 5,000 classes. With such a large landscape, there are more chances to hit "performance landmines."
The Art of Performance Tuning
Many components determine application performance. These include the language calculation performance, database, network, and memory paging characteristics. With procedural languages such as RPG, the logic and calculations you code in the program have less impact on performance than the other factors.
The Java language adds an additional challenge to what has historically been more of an art than a science. When you are analyzing a Java application, you are measuring both the language that you coded and the runtime Java platform. Most of the Java platform is implemented in Java. Compare this with RPG or COBOL in which most language verbs make one direct call into the operating system and one more call below the Machine Interface (MI) to complete the function. In Java, many classes use other classes to complete the function.
One problem with all performance tools (including Java) is that they don't just summarize the results and make a simple recommendation on what to change. The best any performance tool can do is tell you what is expensive relative to everything else you have measured.
In Java, for example, there are many ways to complete a functional requirement. In the example in this article, string concatenation is relatively slow if you use the String object. Using a String buffer object, however, dramatically improves the results. Unfortunately, there are no performance tools that make such suggestions.
The Nature of Java
A large part of the Java runtime environment is written in Java. This layering approach has allowed significant improvements in Java performance without impact to existing applications. Too much layering however, can have a negative effect on performance and can make performance tuning a challenge. Here are some of the basic concepts that can dramatically affect Java performance.
Java is more about objects than its complex parent C++. Everything in Java, with the exception of a few primitive data types, is a formal object. The time it takes to create a Java object depends primarily on the speed of the underlying operating system heap management. In spite of extensive research and tuning, Java applications are primarily influenced by the number of objects created. In a server environment, each object created must eventually be collected through the garbage collection scheme, or you will run out of memory. Determining the number of objects created can be useful information when analyzing Java application performance.
The Java language makes extensive use of synchronization locking. Java is a threaded language, meaning that every object has the potential of being shared between multiple executing threads. To protect the data in the object, Java uses synchronization locks. Synchronization locks are rather fast in Java and have been improved significantly since the early Java Virtual Machines (JVMs). Synchronization locks don't exist, however, in a job-oriented RPG program. Often, Java objects are locked by the JVM when the object will never be shared. Determining whether you are using Java classes, which are making unnecessary synchronization locks, can help you improve application performance.
Object-oriented languages such as Java make extensive use of method calls. Since data is protected inside the boundaries of an object, method calls are used to get and set every data item. Tracing method calls is the most common technique used in tuning Java applications. Tracing Java method calls can produce tens of thousands of trace records in only a few seconds. Locating performance problems often requires a sophisticated tool to reduce and summarize these trace records.
Extensive call activity combined with a large class library and hidden object creation events are what make Java performance tuning a unique challenge.
Performance Tools
Performance Explorer (PEX) is an AS/400 operating system component. It has evolved over several releases starting with the RISC systems in V3R6. Today, PEX supports over 170 different trace events. An industry-distinguishing feature of PEX is its ability to trace many different types of system performance events in one measurement. For example, a PEX measurement can include Java and RPG calls, low-level disk activity, and Java object creation events.
This article will show how PEX can be used to trace Java performance events. Analysis of the trace records can be simplified by limiting the collection time and scope. I will demonstrate how to simplify the analysis by starting and stopping the collection at debug break points. Analysis of object creation and garbage collection can be accomplished by querying the trace records.
Analysis of method call traces require tools that accumulate statistics from many trace records. Performance Trace Data Visualizer (PTDV) is a tool that can be used to simplify the analysis of AS/400 Java performance events. PTDV is available through the IBM alphaWorks Web site at www.alphaworks.ibm.com/tech/ptdv. Java performance tools on platforms such as Microsoft Windows NT and UNIX are driven by a profiling option built into the JVM. The Java platform defines standard trace records produced when the profiling option is selected. The AS/400 JVM does not directly support this profiling option because PEX trace records contain more information. To use a standard Java performance tool, you must convert the PEX trace records to the standard Java format. The
AS/400 JVM provides an application called the Java Performance Data Converter (JPDC) that will convert PEX trace records to the Java industry standard format.
IBM provides a cross-platform Java performance tool called Jinsight that can process the output from the JPDC. The tool is also available through the IBM alphaWorks Web site at www.alphaworks. ibm.com/tech.
Performance Explorer Concepts
There are several concepts associated with running Performance Explorer. These are illustrated in Figure 1. The ADDPEXDFN (Add Performance Explorer Definition) command is used to define a measurement definition. The definition specifies the performance events and determines whether the measurement is limited to specific jobs on the system.
The STRPEX (Start Performance Explorer) command starts a PEX session. The session monitors the JVM, recording trace events in low-level memory below the MI. You can start the trace either before your Java application is started or during certain critical steps in your application.
The ENDPEX (End Performance Explorer) command stops the trace activity and saves the trace data in a set of database files The files can be placed in any library you specify. Each file name starts with the letters QAYPExxxx.
Java Performance Events
The ADDPEXDFN command supports 18 Java performance events. You can see all of the events by prompting on the ADDPEXDFN command. These events are always monitored by the JVM. You can trace them on any Java application whether it is written by you or purchased.
Java method call tracing requires a special hook that is compiled into the Java code through the Create Java Program (CRTJVAPGM) command. Options on the ADDPEXDFN command determine whether you want to trace method calls. Combining method call tracing with base Java performance events will produce a wealth of information.
Not all of the 18 performance events are performance-critical. In writing this article, I discovered that some of the events are not supported by IBM. Refer to the tables at www.midrangecomputing. com/mc in the MC Web edition for the list of events that you can use. The events that are most useful are the *OBJCRT (Object Creation) event and the *GBGCOLSWEEP (Garbage collection) event.
Method calls can be traced by specifying *JVAENTRY and *JVAEXIT. To activate these events, the Java program must be created by specifying the enable performance collection keyword (ENBPRFCOL(*ENTRYEXIT)) on the Create Java Program (CRTJVAPGM) command.
Collecting Object Creation Events
To show how to use PEX on a Java program, I will run PEX on a simple Java program (shown in Figure 2) that concatenates two strings. String objects implicitly create temporary objects behind the scenes. It's not obvious from looking at the Java code that this is happening.
This example demonstrates how PEX can be used to uncover what is happening in the JDK runtime. It proves what is already common knowledge to Java performance gurus: Strings are expensive because they create lots of temporary objects.
Sample Collection
To collect performance data with PEX, use the following steps:
1. Create a PEX definition to trace the object creation events as shown in Figure 3. It's a good idea to specify the job name and user if you can determine the name before the test is run. In this case, QJVACMDSRV is the name of the job created by the AS/400 JAVA or RUNJVA command. This name will be different if you are using Java from QSHELL or from a servlet within WebSphere. If you are testing on a system that you control, you can specify *ALL for the JOB parameter and not worry about collecting too much data. On a busy development system, you should narrow the scope of the collection by specifying a job name. It's good practice to specify MAXSTG to some reasonable limit. This controls the amount of storage used for the trace records. When the trace buffer is full, the trace automatically stops. This can prevent runaway traces from consuming your system. A good value to specify is between 100 KB and 1000 KB. The JAVEVT keyword is used to specify the Java performance events. In this case, *OBJCRT is the object creation event.
2. Start PEX. If you start it before your application is started, you will trace many Java events that are part of the startup processing for the JVM. To avoid this, you can set a breakpoint in the Java program just before and after the section of the program to be monitored. In the sample program, breakpoints were set just before and after the for loop. You can start the Java application and enter debug mode by specifying OPTION(*DEBUG) as in Figure 4. Once you reach the breakpoint, start PEX as in the following:
STRPEX
SSNID(TESTONE) DFN(CNTOBJCRT)
3. Run the Java application. Simply use the resume key PF12, from the debug screen. When the section of code you are measuring ends, you can end the collection by entering the command in Figure 5.
The ENDPEX command stops the collection process. Trace records are moved from the trace buffer below the MI into a set of files in the library specified by the DTALIB keyword. It may take a long time to process this command. The DTAMBR holds the performance data. To simplify querying the files, you might want to store the performance data in the first and only member of each file. ENDPEX will let you save data in any member of the trace files, but then you have to enter overrides or copy the data before you can use SQL to analyze the results. Specifying RPLDTA(*YES) saves time if you are repeating the test because it reuses the existing file and data member.
Analyzing AS/400 Java Performance Results
Analyzing the results means looking for those bottlenecks that you suspect might be occurring but are hard to locate. All of the analysis starts with the information in the PEX database, where there are 52 different files. The files are separated into categories based on the type of performance collection. All of the files begin with QAYPExxxx and are created the first time you use the ENDPEX command on a new library.
Java performance events fall into two categories. Virtual machine events such as object creation and garbage collection can usually be analyzed by querying the trace records. A special tool is not required, but you need to understand the files and fields in the PEX database.
It is important to remember that analysis is easier if you limit the PEX definition to events and jobs that are most meaningful. In some cases, a tool such as PTDV will give you sample definitions that work best for that tool. Remember that it is not possible to analyze something you did not collect, and it is tedious and confusing to analyze too much data. Trying to find the right combination of collection definition and analysis technique is somewhat of a trial-and-error process; be prepared to repeat your experiment.
As an example of querying PEX data files, Figure 6 shows a query that counts the number of objects created and computes the total amount of memory allocated for those
objects in the sample program. The program was called with a parameter of 100 using the definition shown in Figure 2.
The query shows that there were 300 objects created in the loop. Field OJVOBN in file QAYPEJVA contains the size of each object. This query sums the values in this field for all of the created objects. This result means that the loop statement message = first + last; creates three objects and allocates about 50 bytes of memory for each object.
To make use of the trace records, your tool uses the trace record fields in the QAYPExxxx files. A complete layout of all trace records is not possible because some of the fields can only be processed by internal IBM tools. However, some useful fields may be queried as shown above. A table documenting the most useful fields has been provided with assistance from IBM. The tables are located at www.midrangecomputing.com/mc in the MC Web edition.
Java Performance Tools Summary
For Java to grow beyond the experimental stages, serious performance tuning efforts will be necessary. The AS/400 Performance Explorer can be used to collect a myriad of performance data. Much of the challenge, however, is in reducing the performance data to meaningful information.
Simple queries can be used to count events. The performance data files contain name-mapping information used to correlate trace events with method and class file names. A more useful analysis would include grouping the events by method call and attaching method and class file names to the analysis reports. One tool that does a good job of reducing the data and matching performance events to method and class names is Performance Trace Data Visualizer, which presents Java performance data in a GUI. It is not a supported IBM product, but you can download PTDV from the IBM alphaWorks Web site.
PTDV presents data primarily in tabular formats, and cumulative statistics are provided. Java performance analysis requires sophisticated tools. Knowledge of typical problem areas such as object creation and synchronization combined with tracing tools will be required to improve application performance. The usability of the tools will determine whether the fine art of tuning can be used by a wide range of application programmers. Current industry and AS/400 Java performance tools are not ready for everyone. Ad hoc techniques and multiple tools are often required to resolve the problem. As tools improve, however, Java application performance will also improve.
Performance Data Collector
ADDPEXDFN STRPEX ENDPEX Java Virtual Machine
User Queries
PTDV
JPDC
Industry Tools
Java Application Definitions Trace Data
Figure 1: This is the AS/400 Performance Explorer (PEX) collection process.
class WorldWideWait
{
private static String message; // a reference field
// Note: java.lang.String instances are immutable
private static String first = new String("World Wide");
private static String last = new String(" Wait");
public static void main(String[] args)
{
int loop = Integer.parseInt(args[0]); // get count
long start = System.currentTimeMillis(); // start time
for (int i = loop ; i > 0 ; i--) {
message = first + last; // creates a new String!
// Note: We just stored into the same field that
// referred to our previously created 'message,'
// making it 'unreachable'
}
long stop = System.currentTimeMillis(); // stop time
System.out.println((stop - start) + " " + message);
}
Figure 2: This is a sample Java application.
ADDPEXDFN
DFN(CNTOBJCRT)
TYPE(*TRACE)
JOB(PREMTEMA/QJVACMDSRV)
TASK(*ALL)
MAXSTG(1000)
TRCTYPE(*SLTEVT)
MCHINST(*NONE)
JVAEVT(*OBJCRT)
Figure 3: Create a Performance Explorer Definition.
JAVA
CLASS(WorldWideWait)
PARM(100)
CLASSPATH('/Remtema/Java/WorldWideWait')
OPTION(*DEBUG)
SELECT COUNT(*), SUM(QJVOBN) FROM MYPERFDATA/QAYPETIDX TRACE,
MYPERFDATA/QAYPETASKI TASK,
MYPERFDATA/QAYPEJVA JAVA
WHERE TRACE.QTIFTC = TASK.QTSTCT
AND TRACE.QRECN = JAVA.QRECN
AND TASK.QTJUS = 'PREMTEMA'
AND TRACE.QTISTY = 2
> 300 15,600
Figure 4: Run the WorldWideWait Java program.
ENDPEX
SSNID(TESTONE)
DTALIB(MYPERFDATA)
DTAMBR(PERFDATA)
RPLDTA(*YES)
Figure 5: This shows the command to end a PEX session.
Figure 6: This query counts the number of objects created by the WorldWideWait program.
LATEST COMMENTS
MC Press Online