Saturday, August 02, 2008

Improving Java Swing Performance

I propose a method to improve java swing performance by force-loading commonly used classes.

It is commonly thought that Java is slow and that Swing GUI applications are slow however after the JVM has been running for some time this is shown to be incorrect. It is obvious that poorly designed Swing applications will be slow however is there anything we can do to speed up well designed swing applications?

Part of my perception of a java application's "slow speed" is due to the fact that java loads the classes it needs when the class is used. This means that when you use a feature for the first time the JVM will spend a small amount of time loading the necessary classes. Milliseconds can affect our perception of performance. The solution is to simply move this processing elsewhere and force load these classes before first use.

I recommend loading these classes while the splash screen is being displayed.

How?

1. Track what classes are used
2. Identify which classes you want to load during your splash sequence
3. Load your classes

1. Track what classes are used

The following class is used to instrument java and track class loader activity. Pack the following files into a jar along with the manifest and add the following to your application's command line:
-javaagent:MyInstrument.jar=dummyArg


eg:
java -javaagent:MyInstrument.jar=dummyArg -jar MyApp.jar


ClassLogger.java
public class ClassLogger implements ClassFileTransformer {
String output;

ClassLogger(String output) throws Throwable {
this.output = output;
}

private void log(String s) throws FileNotFoundException {
PrintWriter writer =
new PrintWriter(
new FileOutputStream(new File(output),true));
writer.println(s);
writer.close();
}

public static void premain(
String agentArgs,
Instrumentation inst) throws Throwable {
System.out.println("Class Logger Active");
inst.addTransformer(new ClassLogger("loaded.txt"));
}

public byte[] transform(
ClassLoader loader,
String className,
Class classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
throws IllegalClassFormatException {
try {
log(className.replaceAll("/","."));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return classfileBuffer;
}
}

manifest.mf
Premain-Class: tim.build.instrument.ClassLogger


2. Identify which classes should be force loaded

The above will create a file loaded.txt which will contain a list of classes. You will have to review this class list and remove any classes that should not be loaded.

It is easy to identify common functions. Consider using the most common features in the application and forcing these to be preloaded.

Classes created by CGLIB are dynamically generated and do not exist in your application. These should be removed from the list of classes.

3. Load classes during the splash sequence

The following code will have to be customised based upon your application. Basically it loads the list of classes and executes Class.forName on each of them. Java does have java.lang.Compiler which has a method compileClass(Class) and, although I call it below, I believe that it does nothing with JDK6 (I secretly hope it improves performance).

public static void precacheClasses() {
try {
InputStream is = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream("precache.txt");
try {
java.lang.Compiler.enable();
} catch (Throwable t) {
log.warn("Compiler force JIT is not enabled",t);
}
if (is != null) {
String s = IOUtils.toString(is);
for (String sv:s.split("\n")) {
String value = sv.trim();
if (value.length()>0
&& value.charAt(0) != '#') {
try {
Class clazz = Class.forName(value);
java.lang.Compiler.compileClass(clazz);
} catch (Throwable t) {
log.warn("Could not compile class ["+
value+"]",t);
}
}
}
}
} catch (Throwable t) {
log.warn("Problem trying to precache classes",t);
}
}

Usage

Small Applications: Force load every class used. A small utility may be used many times and there is no chance for the JVM to store pre-loaded classes.

Larger Applications: Force load only the classes related to the most commonly used features because of memory constraints.

Success

This makes a well designed application snappy right from the start.

Enjoy,

-Tim

No comments: