Oracle9i Java Developer's Guide
Release 1 (9.0.1)

Part Number A90209-01
Go To Documentation Library
Home
Go To Product List
Book List
Go To Table Of Contents
Contents
Go To Index
Index

Master Index

Feedback

Go to previous page Go to next page

6
Oracle9i Java Application Performance

You can increase your Java application performance through one of the following methods:

Natively Compiled Code

The Java language was designed for a platform-independent, secure development model. To accomplish these goals, some execution performance was sacrificed. Translating Java bytecodes into machine instructions degrades performance. To regain some of the performance loss, you may choose to natively compile certain classes. For example, you may decide to natively compile code with CPU intensive classes.

Without native compilation, the Java code you load to the server is interpreted, and the underlying core classes upon which your code relies (java.lang.*) are natively compiled.

Native compilation provides a speed increase ranging from two to ten times the speed of the bytecode interpretation. The exact speed increase is dependent on several factors, including:

Because Java bytecodes were designed to be compact, natively compiled code can be considerably larger than the original bytecode. However, because the native code is stored in a shared library, it is shared among all users of the database.

Most JVMs use Just-In-Time compilers that convert the Java bytecodes to native machine instructions when methods are invoked. The Accelerator uses an Ahead-Of-Time approach to recompiling the Java classes.

Native Compiler  Description 

Just-In-Time 

Provides the JVM the ability to translate the Java instructions just before needed by the JDK. The benefits depends on how accurately the native compiler anticipates code branches and the next instruction. If incorrect, no performance gain is realized. 

Ahead-Of-Time 

The Accelerator natively compiles all Java code within a JAR file into native shared libraries, which are organized by Java package, before execution time. At runtime, Accelerator checks if a Java package has been natively compiled; and if so, uses the machine code library instead of interpreting the deployed Java code. 

This static compilation approach provides a large, consistent performance gain, regardless of the number of users or the code paths they traverse on the server. After compilation, the tool loads the statically compiled libraries into Oracle9i, which are then shared between users, processes, and sessions.

Accelerator Overview

Most Ahead-Of-Time native compilers compile directly into a platform-dependent language. For portability requirements, this was not feasible. Figure6-1 illustrates how the Accelerator translates the Java classes into a version of C that is platform-independent. The C code is compiled and linked to supply the final platform-dependent, natively compiled shared libraries or DLLs.

Figure 6-1 Native Compilation Using Accelerator


Text description of ncomp.gif follows
Text description of the illustration ncomp.gif

Given a JAR file, the Accelerator performs the following:

  1. Verifies the classes that are loaded in the database.

  2. Retrieves the Java bytecodes for these classes from the database and stores them in a project directory where the Accelerator was invoked.

  3. Translates the Java bytecodes to C code.

  4. Compiles and links the C code using the C compiler for your platform.

    The Accelerator translates, compiles, and links the retrieved classes on the client. For this reason, you must natively compile on the intended platform environment to which this application will be deployed. The result is a single deployment JAR file for all classes within the project.

  5. The resulting shared library is loaded into the
    $ORACLE_HOME/javavm/admin directory.


    Note:

    The Accelerator natively compiled libraries can be used only within Oracle9i. Also, these libraries can only be used within the same version of Oracle9i in which it was produced. If you want your application to be natively compiled on subsequent releases, you must recompile these classes. That is, native recompilation of existing libraries will not be performed automatically by any upgrade process. 


Oracle9i Core Java Class Libraries

All core Java class libraries and Oracle-provided Java code within Oracle9i is natively compiled for greater execution speed. Java classes exist as shared libraries in $ORACLE_HOME/javavm/admin, where each shared library corresponds to a Java package. For example, orajox8java_lang.so on Solaris and orajox8java_lang.dll on Windows NT hold java.lang classes. Specifics of packaging and naming can vary by platform. The Oracle9i JVM uses natively compiled Java files internally and opens them, as necessary, at runtime.

Natively Compiling Java Application Class Libraries

The Accelerator can be used by Java application products that need performance increased and are deployed in Oracle9i. The Accelerator command-line tool, ncomp, natively compiles your code and loads it in Oracle9i. However, in order to use ncomp, you must first provide some initial setup.

Installation Requirements

You must install the following before invoking Accelerator:

  1. Install a C compiler for the intended platform on the machine where you are running ncomp.

  2. Verify that the correct compiler and linker commands are referenced within the System*.properties file located in the $ORACLE_HOME/javavm/jahome directory. Since the compiler and linker information is platform-specific, the configuration for these items is detailed in the README for your platform.

  3. Add the appropriate JDK JAR files, library, and binary information in the following environment variables:

    Environment Variables  Addition Required 

    JAVA_HOME 

    Set to the location where your JDK is installed.  

    CLASSPATH 

    Include the appropriate JDK JAR files in your CLASSPATH as follows:

    • For JDK 1.1, include $JAVA_HOME/lib/classes.zip.

    • For JDK 1.2, include the $JAVA_HOME/lib/tools.jar and $JAVA_HOME/lib/dt.jar files.

     

    PATH 

    Add the JDK binary path: $JAVA_HOME/bin 

    LD_LIBRARY_PATH 

    Add the JDK library path: $JAVA_HOME/lib

  4. Grant the user that executes ncomp the following role and security permissions:


    Note:

    DBA role contains both the JAVA_DEPLOY role and the FilePermission for all files under $ORACLE_HOME


    1. JAVA_DEPLOY: The user must be assigned to the JAVA_DEPLOY role in order to be able to deploy the shared libraries on the server, which both the ncomp and deploync utilities perform. For example, the role is assigned to DAVE, as follows:

      SQL> GRANT JAVA_DEPLOY TO DAVE;
      
      
    2. FilePermission: Accelerator stores the shared libraries with the natively compiled code on the server. In order for Accelerator to store these libraries, the user must be granted FilePermission for read and write access to directories and files under $ORACLE_HOME on the server. One method for granting FilePermission for all desired directories is to grant the user the JAVASYSPRIV role, as follows:

      SQL> GRANT JAVASYSPRIV TO DAVE;
      
      

      See the Security chapter in the Oracle9i Java Developer's Guide for more information JAVASYSPRIV and granting FilePermission.

Executing Accelerator

The following sections show how to do basic native compilation using Accelerator. The Oracle9i Java Tools Reference fully describes all ncomp options.


Note:

Before you natively compile your Java server code, you must have already loaded and tested it within Oracle9i. Native compilation of untested code is not recommended.

Keep in mind that debuggers, such as the debugger provided with JDeveloper, are useful only with interpreted Java code. You cannot debug a natively compiled library. 


All the Java classes contained within a JAR file must already be loaded within the database. Execute the ncomp tool to instruct Accelerator to natively compile all these classes. The following code natively compiles all classes within the pubProject.JAR file:

ncomp -user scott/tiger pubProject.JAR


Note:

Because native compilation must compile and link all your Java classes, this process may execute over the span of a few hours. The time involved in natively compiling your code depends on the number of classes to compile and the type of hardware on your machine. 


If you change any of the classes within this JAR file, Accelerator recompiles the shared library for the package that contains the changed classes. It will not recompile all shared libraries. However, if you want all classes within a JAR file to be recompiled--regardless of whether they were previously natively compiled--execute ncomp with the -force option, as follows:

ncomp -user scott/tiger -force pubProject.JAR

For more options, see the Oracle9i Java Tools Reference.

Java Memory Usage

The typical and custom database installation process furnishes a database that has been configured for reasonable Java usage during development. However, runtime use of Java should be determined by the usage of system resources for a given deployed application. Resources you use during development can vary widely, depending on your activity. The following sections describe how you can configure memory, how to tell how much SGA memory you are using, and what errors denote a Java memory issue:

Configuring Memory Initialization Parameters

You can modify the following database initialization parameters to tune your memory usage to reflect more accurately your application needs:

Oracle9i's unique memory management facilities and sharing of read-only artifacts (such as bytecodes) enables HelloWorld to execute with a per-session incremental memory requirement of only 35 KB. More stateful server applications, such as the Oracle9i ORB that CORBA and EJB applications use, have a per-session incremental memory requirement of approximately 200 KB. Such applications must retain a significant amount of state in static variables across multiple calls. Refer to the discussion in the "End-of-Call Migration" section for more information on understanding and controlling migration of static variables at end-of-call.

Initializing Pool Sizes within Database Templates

You can set the defaults for JAVA_POOL_SIZE and SHARED_POOL_SIZE in the database installation template. The Database Configuration Assistant (DBCA) allows you to modify these values within the Memory section, as shown below in Figure6-2.

Figure 6-2 Configuring Oracle9i JVM Memory Parameters


Text description of dbca_poo.gif follows.
Text description of the illustration dbca_poo.gif

Java Pool Memory

Java pool memory is used in server memory for all session-specific Java code and data within the JVM. Java pool memory is used in different ways, depending on what mode the Oracle9i server is running in.

Java pool memory used within a dedicated server

The following is what constitutes the Java pool memory used within a dedicated server:

Under dedicated servers, which is probably the case for applications using only Java Stored Procedures, the total required Java pool memory is not much more than 10 MB.

Java pool memory used within a shared server

The following is what constitutes the Java pool memory used within a shared server:

Under shared servers, which is the case for applications using CORBA or EJB, this figure could be very large. Java-intensive, multi-user benchmarks could require more than 1 GB. Current size limitations are unknown; however, it is platform dependent.


Note:

If you are compiling code on the server, rather than compiling on the client and loading to the server, you might need a bigger JAVA_POOL_SIZE than the default 20 MB. EJB deployment uses the Java compiler on the server; therefore, it also requires a larger JAVA_POOL_SIZE. 


Displaying Used Amounts of Java Pool Memory

You can find out how much of Java pool memory is being used by viewing the V$SGASTAT table. Its rows include pool, name, and bytes. Specifically, the last two rows show the amount of Java pool memory used and how much is free. The total of these two items equals the number of bytes that you configured in the database initialization file.

SVRMGR> select * from v$sgastat;

POOL        NAME                       BYTES
----------- -------------------------- ----------
            fixed_sga                       69424
            db_block_buffers              2048000
            log_buffer                     524288
shared pool free memory                  22887532
shared pool miscellaneous                  559420
shared pool character set object            64080
shared pool State objects                   98504
shared pool message pool freequeue         231152
shared pool PL/SQL DIANA                  2275264
shared pool db_files                        72496
shared pool session heap                    59492
shared pool joxlod: init P                   7108
shared pool PLS non-lib hp                   2096
shared pool joxlod: in ehe                4367524
shared pool VIRTUAL CIRCUITS               162576
shared pool joxlod: in phe                2726452
shared pool long op statistics array        44000
shared pool table definiti                    160
shared pool KGK heap                         4372
shared pool table columns                  148336
shared pool db_block_hash_buckets           48792
shared pool dictionary cache              1948756
shared pool fixed allocation callback         320
shared pool SYSTEM PARAMETERS               63392
shared pool joxlod: init s                   7020
shared pool KQLS heap                     1570992
shared pool library cache                 6201988
shared pool trigger inform                  32876
shared pool sql area                      7015432
shared pool sessions                       211200
shared pool KGFF heap                        1320
shared pool joxs heap init                   4248
shared pool PL/SQL MPCODE                  405388
shared pool event statistics per sess      339200
shared pool db_block_buffers               136000
java pool   free memory                  30261248
java pool   memory in use                19742720
37 rows selected.

Correcting Out of Memory Errors

The two common memory errors that can occur are as follows:

Running out of memory while compiling

If you run out of memory while compiling (within loadjava or deployejb), you will see the following error:

A SQL exception occurred while compiling: : ORA-04031: unable to allocate  bytes 
of shared memory ("shared pool","unknown object","joxlod: init h", "JOX: ioc_
allocate_pal") 

The solution is to shut down your database and reset JAVA_POOL_SIZE to a larger value. The mention of "shared pool" in the error message is a misleading reference to running out of memory in the "Shared Global Area". It does not mean that you should increase your SHARED_POOL_SIZE. Instead, you must increase your JAVA_POOL_SIZE, restart your server, and try again.

Running out of memory while loading

If you run out of memory while loading classes, it can fail silently, leaving invalid classes in the database. Later, if you try to invoke or resolve any invalid classes, you will see ClassNotFoundException or NoClassDefFoundException exceptions being thrown at runtime. You would get the same exceptions if you were to load corrupted class files. You should perform the following:

End-of-Call Migration

Oracle9i preserves the state of your Java program between calls by migrating all objects that are reachable from static variables into session space at the end of the call. Session space exists within the client's session to store static variables and objects that exist between calls. Oracle9i JVM performs this migration operation at the end of every call, without any intervention by you.

This migration operation is a memory and performance consideration; thus, you should be aware of what you designate to exist between calls, and keep the static variables and objects to a minimum. If you store objects in static variables needlessly, you impose an unnecessary burden on the memory manager to perform the migration and consume per-session resources. By limiting your static variables to only what is necessary, you help the memory manager and improve your server's performance.

To maximize the number of users who can execute your Java program at the same time, it is important to minimize the footprint of a session. In particular, to achieve maximum scalability, an inactive session should take up as little memory space as possible. A simple technique to minimize footprint is to release large data structures at the end of every call. You can lazily recreate many data structures when you need them again in another call. For this reason, the Oracle9i JVM has a mechanism for calling a specified Java method when a session is about to become inactive, such as at end-of-call time.

This mechanism is the EndOfCallRegistry notification. It enables you to clear static variables at the end of the call and reinitialize the variables using a lazy initialization technique when the next call comes in. You should execute this only if you are concerned about the amount of storage you require the memory manager to store in between calls. It becomes a concern only for more complex stateful server applications you implement in Java.

The decision of whether to null-out data structures at end-of-call and then recreate them for each new call is a typical time and space trade-off. There is some extra time spent in recreating the structure, but you can save significant space by not holding on to the structure between calls. In addition, there is a time consideration, because objects--especially large objects--are more expensive to access after they have been migrated to session space. The penalty results from the differences in representation of session, as opposed to call-space based objects.

Examples of data structures that are candidates for this type of optimization include:

Oracle-Specific Support for End-of-Call Optimization

You can register the static variables that you want cleared at the end of the call when the buffer, field, or data structure is created. Within the Oracle-specified oracle.aurora.memoryManager.EndOfCallRegistry class, the registerCallback method takes in an object that implements a Callback object. The registerCallback object stores this object until the end of the call. When end-of-call occurs, Oracle9i JVM invokes the act method within all registered Callback objects. The act method within the Callback object is implemented to clear the user-defined buffer, field, or data structure. Once cleared, the Callback is removed from the registry.


Note:

If the end of the call is also the end of the session, callbacks are not invoked, because the session space will be cleared anyway. 


The way that you use the EndOfCallRegistry depends on whether you are dealing with objects held in static fields or instance fields.

A weak table holds the registry of end-of-call callbacks. If either the Callback object or value are not reachable (see JLS section 12.6) from the Java program, both object and value will be dropped from the table. The use of a weak table to hold callbacks also means that registering a callback will not prevent the garbage collector from reclaiming that object. Therefore, you must hold on to the callback yourself if you need it--you cannot rely on the table holding it back.

You can find other ways in which end-of-call notification will be useful to your applications. The following sections give the details for methods within the EndOfCallRegistry class and the Callback interface:

EndOfCallRegistry.registerCallback method

The registerCallback method installs a Callback object within a registry. At the end of the call, Oracle9i JVM invokes the act methods of all registered Callback objects.

You can register your Callback object by itself or with a value object. If you need additional information stored within an object to be passed into act, you can register this object within the value parameter.

public static void registerCallback(Callback thunk, Object value);
public static void registerCallback(Callback thunk);

Parameter   Description 

thunk 

The Callback object to be invoked at end-of-call migration. 

value 

If you need additional information stored within an object to be passed into act, you can register this object within the value parameter. In some cases, the value parameter is necessary to hold state the callback needs. However, most users do not need to specify a value

EndOfCallRegistry.runCallbacks method
static void runCallbacks()

The JVM calls this method at end-of-call and calls act for every Callback object registered using registerCallback. You should never call this method in your code. It is called at end-of-call, before object migration and before the last finalization step.

Callback Interface
Interface oracle.aurora.memoryManager.Callback

Any object you want to register using EndOfCallRegistry.registerCallback implements the Callback interface. This interface can be useful in your application, where you require notification at end-of-call.

Callback.act method
public void act(Object value)

You can implement any activity that you require to occur at the end of the call. Normally, this method will contain procedures for clearing any memory that would be saved to session space.

Memory Profiling Utility

The purpose of the Memory Profiling Utility (MemStat) is to trace, profile, and report on the allocated memory that is accessible through static variables in your Oracle9i Java program. You can then use the information in this report to locate and eliminate unnecessary static data in your Java classes, thereby reducing the static footprint of your Java program and improving the performance of repeated Java calls into the database.

The Oracle9i JVM uses three kinds of memory:

Java language semantics specify that static variables persist across calls. At the end of each call, the Oracle9i JVM copies the call memory that is accessible through the static variables in each class into session memory so that it can be saved and restored on subsequent calls to methods in those Java classes. If there is a lot of static data or a complex graph of interconnected objects, then there is considerable overhead during the end-of-call processing while the JVM allocates session memory and copies the static data to it.

A typical technique for tuning object-oriented programs for faster performance is to eliminate the allocation of unnecessary objects from your program. For example, you can create a static instance of a commonly used object and reuse it rather than creating a new one every time you need it. However, the interactions among the different database memories complicate such techniques, and can require analysis of the speed trade-off for allocating dynamic objects versus the space trade-off for the end-of-call copying of static objects. If a static object is large, or if there are many such objects, or if there are many calls, then the speed advantage gained by caching the object may be lost, due to the traversal of the object graph during end-of-call processing.

How MemStat Works

Depending on how you invoke it, MemStat will analyze either a single class or all classes that are loaded into the current session. For each class, MemStat enumerates the static variables of the class. These variables are known as the roots. Depending on the structure of each variable, MemStat performs three different analyses:

This process is repeated recursively until all objects reachable from all static variables have been recorded. Because it is possible for large object graphs to contain cycles, MemStat also records any circular references it encounters during the analysis.

Using MemStat

The purpose of MemStat is to analyze and report on the object graph that is accessible from the static variables in your program. You can invoke the analysis directly from any point in your program, and you can also register it to run at the end of a call.

Because there is no standard output mechanism for database calls, MemStat produces its report in the form of HTML files, in a directory that you specify. When the report is finished, you can view these files with any HTML-capable Web browser.

MemStat is implemented in three static methods on the class oracle.aurora.memstat.MemStat.

You can call it in three different ways:

The method call for reporting on a single class is:

MemStat.writeDump (Class MyClass, String outputPath, String filePrefix);

The method call for reporting on all loaded classes is:

MemStat.writeDump (String outputPath, String filePrefix);

The method call for reporting on all loaded classes at the end-of-call is:

MemStat.writeDumpAtEOC (String outputPath, String filePrefix);

The outputPath parameter represents the directory in which the MemStat reports are generated. The outputPath string must be in a file name format that is suitable to the platform on which the report is generated. For example, /home/base/memstat is suitable for a Solaris platform; the Windows format might be c:\\base\\memstat. Note that Java requires doubling of the backslashes inside a string, but not the forward slashes.

The filePrefix is the base file name for the HTML files that are generated in the outputPath directory. Because MemStat reports can be voluminous, and many Web browsers have limitations on the size of the files they can browse, MemStat breaks long reports into separate files. The filePrefix is the basis for all file names in a given report and is augmented by an incremental numeric suffix. If, for example, the test report produces three files, the main report file will be named test.htm, and additional report files will be named test1.htm and test2.htm.

If you call MemStat more than once in a given call, be careful to use different base names or different output directories, lest the subsequent reports overwrite the previous ones. For example, if you call MemStat before and after you perform some memory-consuming operation, naming the first report before and the second report after will prevent name collisions, while still writing the report files into the same directory. Using multiple directories is more complicated: you must remember to grant separate FilePermissions (see below) for each directory in which you want to write.

Here are some sample MemStat calls:

MemStat.writeDump (MyClass.class, "c:\\base\\memstat", "myclass");
MemStat.writeDump ("/home/base/memstat", "test");
MemStat.writeDumpAtEOC ("/home/base/memstat", "eoc");

MemStat Permissions

MemStat requires certain permissions to be granted to the user or role under which it runs. Because MemStat runs in the Oracle server process, these permissions grant access to the resources that MemStat requires:

The following SQL statements grant these permissions to user JIM:

call dbms_java.grant_permission ('JIM',
'SYS:java.lang.reflect.ReflectPermission', 'suppressAccessChecks', null); call dbms_java.grant_permission ('JIM', 'SYS:oracle.aurora.security.JServerPermission', 'JRIExtensions', null); call dbms_java.grant_permission ('JIM', 'SYS:java.io.FilePermission', '/home/base/memstat', 'read,write'); // Solaris call dbms_java.grant_permission ('JIM', 'SYS:java.io.FilePermission', 'c:\base\memstat', 'read,write'); // Windows

If the Oracle Server is running on a Windows platform, the output file path named in the MemStat call is subtly different from the path in the SQL grant_permission call. In Java strings, you must use double backslashes; in SQL you need only one backslash.

The MemStat Report Format

This section describes the format of the MemStat report. You can browse the MemStat output report with any HTML-capable Web browser. To do this, point the browser at the base file name that is specified. For example, the following points the browser at the test.htm file:

c:\base\memstat\test.htm.

The report begins with a summary of the memory usage at the time MemStat is invoked. This summary should give you an overall idea of the amount of memory used by the Java call that you are analyzing.

Following the summary is a list of the unique classes that are traversed during the MemStat analysis. For every object found during the memory analysis, MemStat has recorded its class and its size in each of call, session, and permanent memory. The largest objects are sorted first, because eliminating these will yield the largest performance improvement. The list is actually sorted by the largest of these three sizes, calculated as max (call, max (session, permanent)). For each class, this table also shows how many bytes are occupied by objects of that class, how many objects there are, their minimum, maximum and average size, and for arrays, the standard deviation of the sizes.

Following the class summary is one or more tables describing each root object. The title of the object describes the package and class of the object. Each row of the table describes:

Following the root objects are the objects pointed to by the roots; the objects are separated by a dividing rule. One, two, or three tables describe each object:

The title for each object describes the memory in which the object resides: Call, Session, or Permanent. Each object is described by:

An object that refers to another object is linked by an HTML link to the tables representing the object to which it refers. You can navigate the object graph using these links, as you would navigate hyperlinks in a text document.

The following shows an example of the output of the MemStat tool:

MemStat Results

2000-06-01 17:07:05.645

Run-Time Values 

Session Size 

143360 

NewSpace Size 

262144 

NewSpace Enabled 

true 

Intern Table Size 

261814 

Total Memory Allocation 

 

Call 

Session 

Permanent 

Objects 

726  

926 

3217 

Total Size 

54861 

39348 

127418 

Minimum 

12  

12 

12  

Maximum 

16396 

2060 

8076 

Average 

75.6 

42.5 

39.6  

Std Deviation 

679.2 

93.7 

233.7 

Allocated Objects by Class (Call, Session, Permanent) 

Class 

Bytes 

Objects 

Minimum 

Maximum 

Average 

Standard Deviation 

char[] 

25316 

5134 

43296 

161 

104 

1177 

16384 

800 

8064 

157.2 

49.4 

36.8 

1,283.2 

105.0 

276.9 

java.lang.String 

3816

 

3240 

30528 

159 

135 

1272 

 

 

 

 

 

 

 

 

 

 

 

 

java.util.Hash
table$Entry 

4956 

10696 

12460 

177 

382 

445 

 

 

 

 

 

 

 

 

 

 

 

 

byte[] 

8195

 

2421 

2107 

34 

57 

8192 

2048 

1024 

4,097.5 

71.2 

37.0 

4,094.5 

344.2 

143.3 

Objects Accessible From
java.util.Properties
 

Field 

Reference 

Total Size 

keyValueSeparators 

946: java.lang.String 

50 

strictKeyValueSeparators 

948: java.lang.String  

40 

specialSaveChars 

950: java.lang.String  

54 

whiteSpaceChars 

952: java.lang.String  

46 

hexDigit 

954: char[16]  

44 


Go to previous page Go to next page
Oracle
Copyright © 1996-2001, Oracle Corporation.

All Rights Reserved.
Go To Documentation Library
Home
Go To Product List
Book List
Go To Table Of Contents
Contents
Go To Index
Index

Master Index

Feedback