Super Prev Next

JetPerfect Global Optimizer

Note: Information in this Chapter is applicable to Excelsior JET Professional Edition only.


Super Prev Next

Introduction

Excelsior JET, Professional Edition features JetPerfect Global Optimizer — a powerful facility that improves application performance and eases deployment. The use of JetPerfect gives you several important advantages:

JetPerfect is integrated with the core JET compiler. By default, JET compiles applications in the dynamic link model, i.e. compiled application classes are linked into an executable that requires one or more JET runtime DLLs containing precompiled Java 2 platform classes (see Profiles). With JetPerfect, both application classes and platform classes are linked into a single executable that does not need those runtime DLLs and does not contain redundant code and data.

Note that JetPerfect-enabled compilation takes considerably more time and requires more resources. We recommend using JetPerfect only after you ensure that your application behaves as expected when compiled with the dynamically linked runtime.

The disadvantage of JetPerfect is the inability to handle dynamic loading of arbitrary classes at run time. A prerequisite to JetPerfect usage is that all classes required by the application are known at compile time. Therefore, you may not use it in the mixed compilation model.

Note: Read the section Application domain carefully to decide whether JetPerfect is suitable for your program. If, due to any reason, you deem its usage unsafe, we recommend you to use the default, “all included” dynamic link model.


Super Prev Next

Application domain

Not all Java applications are eligible for processing by JetPerfect as they have to meet the following criteria:

Therefore, applications configurable at run-time by loading some components or plug-ins are not appropriate. So are, for example, applications exploiting the RMI Distributed Class Model, in which the implementing classes are loaded from remote hosts. Nevertheless, there is still a lot of Java programs that do not use dynamic loading so intensively. Here is a few examples of successful JetPerfect usage:

javac

Sun’s Java source to bytecode compiler coming with Java 2 SDK.
Seruku Toolbar

Seruku Toolbar is a plug-in for Internet Explorer(tm). The core part of Seruku Toolbar is written in the Java programming language. JetPerfect optimization slashed the download size of this plug-in from 10 to 3MB. For more details, see

http://www.seruku.com/Toolbar/Toolbar.html.

IBM SWT

IBM SWT (Standard Widget Toolkit) is an open source Java GUI library alternative to AWT/Swing. In combination with JetPerfect and JRE-less operation it allows you to create GUI-based Java 2 applications that run without a JRE while fitting on a single 1.44MB diskette! See samples/SWT/readme.txt in your JET installation directory for details.


Super Prev Next

What does JetPerfect do?


Super Prev Next

Global optimizations

JetPerfect effectively optimizes the data abstraction penalty incurred by virtual method invocation, type inclusion checks, etc. As a result, it improves the strength of optimizations performed by the JET compiler to a great extent. Analysing the entire program, including the Java 2 platform classes, allows the compiler to make more inline substitutions and allocate more objects on the stack instead of the heap.


Super Prev Next

Code and data extraction

JetPerfect performs global type analysis, makes a safe approximation of actually used classes, methods and fields and then removes redundant code and data. This technique significantly reduces the download size of optimized Java applications.


Super Prev Next

Using JetPerfect at a glance


Super Prev Next

Overview

Unfortunately, JetPerfect cannot automatically accomplish code extraction for classes and methods accessed reflectively. Specifically, it concerns Java Reflection API and/or Java Native Interface (JNI) employed by many applications. This is why a substantial amount of human assistance is necessary to correctly apply global optimizations. On the whole, using JetPerfect comprises the following steps.

As compared to the default dynamic link model, two extra steps are required before creating JetPerfect-optimized executables.


Super Prev Next

Why must you assist JetPerfect?

For example, consider this simple program.

class Bill_G {}
class Bill_J {}

public class hello {

  static public void main (String args[])
    throws ClassNotFoundException
  {
    String subj = (args.length == 0) ?
                  "world" : Class.forName(args[0]).getName();

    System.out.println("Hello " + subj);
  }
}

Depending on the arguments that may be put on the command line (if any), both, one or none of the classes Bill_G and Bill_J must be included in the resulting executable. Walking through this example, it is easy to see that reflective dependencies cannot be automatically obtained by analysing program code.

Thus, in addition to the application’s bytecode, JetPerfect needs a complete list of classes, methods and fields that may be used reflectively during program execution. Given that certain Java 2 platform classes are accessed reflectively (from within native methods via JNI), it is almost impossible to create the list for a large application by hand.

Fortunately, JetPerfect provides facilities for the detection of reflective dependencies at run time. Moreover, you may explicitly declare the entities that must be forced into the resulting executable.


Super Prev Next

JetPerfect step by step


Super Prev Next

How to figure out reflective dependencies

Suppose, you have a JET-compiled application that works as expected in the default dynamic link model. In order to cover its reflective dependencies, you should perform the following steps.


Super Prev Next

STEP 1. Run-time profiling

The JET run-time system is able to create a log including all reflective accesses to program entities occurred during execution. To enable reflection activity logging, set the jet.usage.list property.

Additionally, you set the value of the jet.default.classloader property to bootstrap or application. Without custom classloaders, a JVM loads Java 2 platform classes with the bootstrap classloader and application classes — with the application one. Nevertheless, many applications work flawlessly if the bootstarap classloader loads all classes. The choice of either classloader affects the size of the resulting executable (it will be larger if the application classloader is used). /You may determine which classloader is enough for your application classes as follows. Suppose you run your application on a JVM using the command line java -cp <directories and jars> AppMain. If you run it as java -Xbootclasspath/a:<directories and jars> AppMain and the application’s behavior does not change, the bootstrap classloader would be enough./

For conventional executables and DLLs, setting these properties may be done via the JETVMPROP environment variable at the command line or in a batch file:

SET JETVMPROP=-Djet.usage.list -Djet.default.classloader:bootstrap
MyApp.exe

This however does not work for NT services. If you need to create a log for a service,

set the properties at compile time and rebuild your application.

As a result, reflective dependencies will be automatically collected during the execution of your application. Upon termination, the dependency list will be saved in a file with the executable’s name and the ".usg" extension, created in the directory where the executable resides.

Notes:

  1. An NT service is terminated when you stop it using the NET STOP command or the Services applet of the Windows Control Panel.
  2. When collecting a usage list for a JET-compiled DLL accessed by a native executable via the Invocation API, the usage list is written when DestroyJavaVM() is called. If the process that loaded the DLL terminates without calling DestroyJavaVM(), the usage list will not be saved.

You may re-run your application as many times as you wish, supplying, for instance, different input data. In this case, the previously created .usg file is not overwritten, but newly detected classes, methods and fields are added to it.

Warning: Be careful with updating the log and make sure to remove the previously created .usg file if the application was changed and rebuilt.

Now, stress test your application to achieve the largest possible code coverage. Ideally, you should use all features, all modes of operation, and all their combinations on large amounts of heterogenous input data.

Once the usage list has become stable (no more entries are added to .usg file), proceed to the next step.

Note: Be aware that .usg file is auto-generated so it should not be edited by hand.


Super Prev Next

STEP 2. Forcing classes and members

This step is optional. You perform it only if certain program parts are known to be accessed reflectively during execution, or if you are not sure how they are actually used so it would be safer to force them into the resulting executable explicitly.

The precision at which you define the “must be forced” parts of your application can vary and includes the following options:

A special-case option is locale related classes providing internationalization support such as character conversion, date/time formats, etc. These classes are definitely reflected by the Java 2 platform API. For them, you may simply specify the names of the locale groups necessary for your application (e.g. Japanese, East_European, etc.).

To declare explicitly forced items, you create a plain text file with the ".fus" extension, obeying the syntax described in Section Forced usage file format.


Super Prev Next

JetPerfecting your application

To perform the most size effective code extraction, you specify several optimization settings and then enable global optimization. In essence, the settings control the trade-off between the size of the resulting executable and the functionality available to it. All the options described below must be explicitly set in your project file to perform global optimization.


Super Prev Next

STEP 3. Enabling JetPerfect

  1. If your application works with time zones, switch the IncludeTimeZoneInfo option ON in your project file:

        -IncludeTimeZoneInfo+

    It includes the necessary data into the resulting executable. Otherwise, switch it OFF.

  2. If your application requires support for all attributes (such as date/time format, currency format, etc.) of the locale detected on STEP 1, switch the IncludeDetectedLocales option ON in the project file:

        -IncludeDetectedLocales+

    It adds all required classes for the detected locale and all locales with the same encoding to the resulting executable /You may add support for extra locales listing them in the forced usage file on STEP 2./ .

    Switching this option OFF significantly reduces the size of the executable at the expense of locale support being limited to the current locale only. Also, only those locale attributes used when profiling reflective dependencies will be included into the resulting executable.

  3. If you use JRE 1.4 (or later) and you application uses the Java Logging API (java.util.logging), switch the IncludeLoggingAPI option ON in the project file.

        -IncludeLoggingAPI+

    Otherwise, switch it OFF to reduce the size of the resulting executable.

  4. Append a line specifying the automatically generated .usg file to your project file. For example, if the target executable name (without extension) is MyApp, add the following line to the project file:

        !module MyApp.usg

  5. If you created a forced usage file, MyApp.fus on STEP 2, add the following line to the project file:

        !module MyApp.fus

  6. Finally, make sure the name of your application’s main class is explicitly specified in the project file using the equation MAIN:

        -main=MyAppMainClass

    You have to specify the main class explicitly, because JetPerfect compiles not only your application classes, but also JDK classes, some of which contain method

        static public void main (String[])

    so the compiler is unable to determine the entry point automatically.


Super Prev Next

STEP 4. Compilation

Recompile the entire application with the PERFECT compiler option enabled.

    jc =p MyApp +perfect

Note that compilation will take much longer than in the default mode, because not only application classes but also Java 2 platform classes will be optimized.


Super Prev Next

Troubleshooting

When QA testing the optimized executable, you may encounter the problem that not all reflective dependencies were detected with run-time profiling on the build machine. For instance, the JetPerfect optimization was performed on an NT-based system (Windows NT, 2000, XP) but after porting the executable to a Windows 9x flavour (95, 98, ME) some extra classes are proved to be reflected.

In such case, the executable provides feedback: the message

  JETPerfect: Incomplete .usg file. Extra usage list generated.

is printed to console /If your application was compiled with suppressing the console window, a pop-up window with this message shall appear./ (to the event log for NT services), an extra .usg file is created in the directory where the executable resides, and the application terminates with non-zero return code.

If the optimized executable works improperly but no exta usage list is created, you may try to enable extra checks for detection of missed reflective dependencies by setting the following property:

  SET JETVMPROP=-Djet.report.not.found.entities
  MyApp.exe

It results in creation of an extra .usg file listing the names of the classes that might be completly removed by the optimizer.

If such a problem occurs, you have two options to remedy it.


Super Prev Next

Rebuilding with extra .usg file

This way, you just rename this newly created .usg file, say, to MyApp_extra.usg, move it to the build machine and add this line to the project file:

    !module MyApp_extra.usg

At this point, you must also choose the aggressiveness with which JetPerfect has to complete the list of reflective dependencies. Options are adding exactly those entries listed in the extra .usg file or including all listed classes (with their members) in whole. If you prefer the exact completion of the reflective dependencies, add this equation to the project file

    -USGFILEAUTOCOMPLETION=OPTIMIZED

otherwise, add

    -USGFILEAUTOCOMPLETION=SAFE

for more extensive completion.

Finally, recompile the entire application on the build machine

    jc =p MyApp +perfect

and proceed with your QA testing.


Super Prev Next

Completing the original .usg file

The process of creating extra .usg files is inherently repetitive. Of course, after rebuilding the optimized executable, execution goes further but yet another lack of collected dependencies may be determined. As recompilation with enabling JetPerfect takes a while, in some cases, it would be easier to complete the list using the original executable built without JetPerfect.

To do that, you employ JetPackII to create CD image which includes the original executable and the .usg file created on the build machine. As a result, you have a self-contained directory that you simply copy to the system where the problem occurs. Then you run the original executable and perform the scenario caused the problem in the JetPerfect-optimized executable thus completing the original .usg file. Finally, you move the file to the build machine and recompile the entire application as

    jc =p MyApp +perfect

This technique substantially reduces the number of iterations necessary to create the complete list of reflective dependencies.


Super Prev Next

Deployment


Super Prev Next

Deployment set

JetPerfect compiles your application into a single executable that includes the code of both application and platform classes. If your application does not employ AWT/Swing, the resulting executable contains all the necessary native methods for Java 2 platform classes. In conjunction with JET’s resource binding capabilities these features enable you to deploy JetPerfect-optimized pure Java programs simply by copying the executables to other systems.

However, JET is not capable of linking in arbitrary native methods, as they typically reside in DLLs. Therefore, globally optimized AWT/Swing-based applications and applications employing third-party libraries with native methods require some DLLs to run. But that does not hinder easy deployment. The JetPackII utility distributed along with JET allows you to create a single self-extracting executable including the compiled application, DLLs with native methods, and any resources required by application (image files, fonts, etc.), if they are not bound to the executable (see Resource binding).

Yet another option you may find in JetPackII is creating a CD image, which in particular enables you to deploy your application simply by copying a self-contained directory to other systems.


Super Prev Next

Disabling dependencies logging

For security reasons, you may wish to disable generation of the reflective dependencies list in the end-user version of your application. If so, toggle the option DISABLEUSAGELIST ON in the project file and re-build the executable.


Super Prev Next

Implementation notes


Super Prev Next

Backward compatibility

If you have project and .usg files created with JET versions prior to 3.5, you may continue using them. However, when switching to JET 3.5 (or later versions), you have to add the new global optimization settings to your project files.

Moreover, starting from v3.5 the JET run-time system performs stricter checks of the completeness of the reflective dependencies used for JetPerfect optimization. To disable the checks, you may set the jet.disable.jetperfect.checks property. However, it would be more reliable to perform completion of the list of reflective dependencies using the feedback mode as described in Section Troubleshooting.


Super Prev Next

Known limitations

The following limitations are to be removed in furture versions of JetPerfect.


Super Prev Next

Forced usage file format

A forced usage (.fus) file is a plain text file each non-blank lines of which contains either a section header or section items following the header.

At will, you may force classes (in whole), separate class members, packages, jars, and classes for particular locale groups into the resulting executable.

The respective sections have the following syntax:

# this is a comment

#---------------------------------------------------------------

[Methods]

{<qualifiled method name>[<signature>]}

# e.g. 
#      java/lang/String.equals
#      java/lang/String.<init>(V)V

#---------------------------------------------------------------

[Fields]

{<qualifiled field name>[@<signature>]}

# e.g.
#      com/company/Goo.f
#      com/company/Foo.g@[java/lang/Cloneable;

#---------------------------------------------------------------

[Classes]

{<qualified class name>}

# e.g.
#      java/io/Win32FileSystem
#

#---------------------------------------------------------------

[Packages]

{<package name>}

# e.g.
#      sun/java2d/loops
#      com/company/stuff

#---------------------------------------------------------------

[Jars]

{<jar name>}


# e.g.
#      jh.jar
#      activation.jar   

#---------------------------------------------------------------

[Locales]
{<locale group name>}


# e.g.
#      Korean
#      East_European


Super Prev Next

Classes

The [Classes] section includes class names prepended by their package names (the package delimiter is "/").

Specifying classes, you force all their members into the resulting executable.


Super Prev Next

Methods

The [Methods] section includes method names prepended by their class names (the method name delimiter is "."). The string "<init>" used to represent constructor names.

If a method is overloaded (i.e. there are several methods with the same name and different signatures), its signature should be appended to the method name. The format of the signature is the standard JNI method signature described in the following table:

signature Java type
Z boolean
B byte
C char
S short
I int
J long
F float
D double
V void
Lfully-qualified-class; fully-qualified-class
[type type[]

For example, the method

    int foo (int n, Object o, int[] arr);

has signature

    (ILjava/lang/Object;[I)I


Super Prev Next

Fields

The [Fields] section specifies fields names prepended by their class names (the field name delimiter is ".").


Super Prev Next

Packages

The [Package] section lists package names (the subpackage name delimiter is "/").

Specifying a package, you force all classes from the package and its subpackages, recursively. Note that the scope affected by this section is your application’s packages as well as the Java 2 platform packages. For instance, if you simply specify com package, all packages prefixed with com. will be forced including those from the Java platform API. It is recommended to force packages with a higher precision including subpackage names, e.g. com/company/package.


Super Prev Next

Jars

The [Jars] section contains bare jar file names. Note that you have to add full paths to the jars to the project file via LOOKUP statements. For example,

    -lookup = *.jar = C:\App\DirectoryWithJars

Including a jar file, you force all packages from the jar into the resulting executable.


Super Prev Next

Locales

The [Locales] section may include the following locale group names depending on the JRE version in use:

The locale groups are organized following this principle: all locale-related classes with the same encoding (code page) are put into one group. Specifying a locale group, you enable all required functionality such as character conversion, date/time format, currency format, etc.

Thus, typical content of a .fus file looks as follows:

[Methods]

    java/lang/System.initializeSystemClass
    java/lang/ref/Reference$ReferenceHandler.run

[Classes]

    java/io/Win32FileSystem
    java/lang/Win32Processes

[Packages]

    sun/java2d/loops
    com/company/stuff

[Jars]

    jh.jar
    activation.jar   

[Locales]

    East_European
    Cyrillic
    Latin_1