Java

Using Java 2 JNI on HP-UX

_침묵_ 2006. 12. 6. 06:01

출처 :http://www.hp.com/products1/unix/java/infolibrary/prog_guide/JNI_java2.html

 

Unless specified otherwise, the information in this chapter applies to both HP-UX PA-RISC and HP-UX Itanium® Processor Family systems. Regarding the examples contained in this chapter, do not use +D64 with Java 1.3 based releases on Itanium. Beginning with SDK 1.4.1 you may use +D64.

 

Table of contents

≫ Using Java 2 JNI on HP-UX

  ≫ Support for C++ applications built with -AA options (PA-RISC)

  ≫ Dereferencing NULL pointers

  ≫ Using Native Methods on HP-UX

≫ Java calling a native (non-Java) method
  ≫ Sample code for SDK 1.3.1

    ≫ Sample Java calling native method implementation in C

    ≫ Sample Java calling native method implementation in HP aC++

≫ Native (non-Java) calling Java methods

  ≫ Sample code for SDK 1.3.1

    ≫ Sample native calling Java implementation in C

    ≫ Sample native calling Java implementation in HP aC++

≫ Main/Primordial thread stack size limits

≫ Non-Main/Primordial thread stack size limits

≫ Dynamically loading the Hotspot JVM (SDK 1.3.1.x and 1.4.2.x PA-RISC)

≫ Additional references

≫ Archived documentation Classic JVM

 

 

Using Java 2 JNI on HP-UX

The Java Native Interface, typically referred to as JNI, is a standard native method interface that allows Java to call up native libraries written in other languages. It also allows you to load the JVM into native applications to integrate Java functionality into native applications. JNI offers a JVM-independent programming interface to native programs which enables developers to write a single native method library version that will be binary compatible with all JVMs on a given platform.

For native code support, the native library must be in SHLIB_PATH. HP suggests that you place your native libraries in

<applicationdirectory>/lib/<architecturedirectory>

When developing applications, if you are using native methods, you must compile them into a shareable native library, for example:

<applicationdirectory>/lib/<architecturedirectory>native_threads/foo.sl

The script that invokes the runtime interpreter for your executable should reset the CLASSPATH and JAVA_HOME environment variables to avoid any conflicts with other Java applications or Java runtime versions on your system. You must also include an installer.

The HP-UX implementation of JNI does not differ significantly from that of the JavaSoft implementation on Solaris. Refer to JavaSoft's JNI documentation listings at:http://onesearch.sun.com/search/developers/index.jsp?qt=JNI&uid=6910018

Support for C++ applications built with -AA and -AP options (PA-RISC)

Java supports the -AA and -AP options to build your C++ product. On Itanium systems, the C++ runtime libraries support -AA and -AP by default. On HP-UX 11.0 or 11.11 PA-RISC, if you are using the ANSI Standard C++ runtime (-AA) option in an application that loads Java, you need to use the -AA version of libjvm and libfontmanager. Note that these libraries are provided as a separate download on the same page from where you download the SDK and RTE, starting at this webpage:
http://www.hp.com/products1/unix/java/java2/sdkrte14/downloads/index_pa-risc.html

These are the Standard C++ Runtime version of these libraries:

./jre/lib/PA_RISC2.0/libjvm_v2.sl
./jre/lib/PA_RISC2.0W/libjvm_v2.sl
./jre/lib/PA_RISC2.0/libfontmanager_v2.sl
./jre/lib/PA_RISC2.0W/libfontmanager_v2.sl

The native application must be linked with, or must dynamically load these versions of the Java libraries if your C++ application is compiled using -AA.

The Standard C++ version of the JVM libraries are supported for PA_RISC2.0 and PA_RISC2.0W architectures only.

If the JVM is invoked through the standard Java driver, then use the -V2 option to use the Standard C++ runtime.
For example:

java -V2 <javaprog>

Dereferencing NULL pointers

With the Java 2 HotSpot VM, JNI that incorrectly dereferences NULL will result in a SIGSEGV,and Java NULL checks can be performed without having to emit explicit to do so. (This is different than the Java 1.1 behavior.) With Java 2 HotSpot, the signal is caught, and a null pointer exception is thrown if the offending instruction was within the VM (compiled, or in the interpreter). This method may uncover hidden programming errors.

Also, if you are including the HP-UX Runtime Environment for the Java 2 Platform in an application and bypassing our standard driver, for example by making calls to JNI_CreateJavaVM from inside the application, link the application with the "-z" option. The -z option will indicate that dereferencing NULL pointers in the application should generate a SIGSEGV instead of the traditional behavior of returning zero. If you do not, you will not be able to take advantage of implicit null pointer checks; null pointer checks will have to be explicit, potentially degrading performance. Linking with -z may also expose existing but quiet bugs in an application. This is because the SIGSEGVs were not being generated before.

Using Native Methods on HP-UX

The sections below detail the differences that you need to be aware of when using native methods on HP-UX. Examples are provided for Java 1.3 and 1.4 that illustrate the capabilities for implementation in C and C++.

Note:The default stack size for 1.4 and 5.0 64-bit mode threads created by the JVM is 1MB. On PA-RISC 32 and 64-bit systems, the default stack size is 64KB. Therefore, if you are using C language main programs that attach with JNI, for threads created using the pthread library, you will want to adjust the stack size to avoid overflows. For further details on thread Stack Size Limits, refer to "Main/primordial thread stack size limits" and "Non-Main/primordial thread stack size limits".

Java calling a native (non-Java) method

The first capability that JNI provides is to allow Java running under the Java Virtual Machine (JVM) to make native method calls. This allows the Java programmer to supply a native implementation instead of a Java implementation for any method in any user defined class. The Java keyword native is used in the declaration of the method and no body or definition is supplied in the Java source.

native void sample(int x, int y);

The above declares a non-static native method named "sample" which returns a void and it accepts two integers as parameters.

The native method must be implemented in C or C++. When using C++ native methods special care must be taken to properly initialize the C++ runtime before transferring control to any C++ native method. For the HP C++ compiler this means that the routine _main must be called before transferring control to any C++ method. The examples below detail a mechanism for ensuring that the C++ runtime is properly initialized.

All Java native methods must reside in a shared library. This implies that the native methods must be built using the +z or +Z compiler options. These options cause the compiler to generate PIC (Position-Independent-Code). The shared library containing the native method implementation is dynamically loaded during execution of the Java program using the Java method:
System.loadLibrary(). The implementation of this function with the JVM on HP-UX relies upon the HP-UX runtime routine shl_load.

The alignment requirements for Java data structures allocated in the JVM are not the same as those found in the standard PA-RISC 2.0 calling conventions. The JVM requires that class instance data members 64-bits in size (longs and doubles) be allocated at the next available 4-byte boundary. The standard PA-RISC 2.0 calling conventions normally require that such objects be aligned on the next 8-byte boundary. If a C or C++ native method were to access such a Java class instance data member that was not properly aligned on an 8-byte boundary, a SIGBUS signal would be delivered to the program and this would terminate the JVM. To prevent this situation, use the +u4 option to the C and aCC compilers when compiling native that will directly access Java data members. This command-line option instructs the compiler to use a special sequence when dereferencing pointers to the 64-bit types. The special sequence correctly handles the cases in which a 64-bit type is misaligned.

When building native method implementations to be used with the kernel-threaded JVM, sources must be compiled with the following additional command-line options:

-D_HPUX -D_POSIX_C_SOURCE=199506L

Additionally developers who produce hybrid applications where the application entry point is a language other than Java, for example a C language main(), must link with the p-thread library using -lpthread on the link line in order to use the kernel-threaded JVM. Failing to link with libpthread.sl (libpthread.so on Itanium) will result in an runtime failure when the JVM is loaded. The libpthread library was not available in releases prior to HP-UX 11.0.

Implementers of native methods called by Java main programs should take care to insure that null pointers are not dereferenced. The JVM is linked using the -z option, which is discussed in the man page for the C compiler. If a native method does deference a null pointer a SIGSEGV will be delivered to the JVM and this will cause the JVM to terminate.

HP provides the following two Java examples on how to call C and C++ from Java using the standard JNI calling mechanism. Code examples C++ for Java 1.3.1 and 1.4.2 follow.

Sample code for SDK 1.3.1

Sample Java calling native method implementation in C
Here is the sample Java program that calls a native method, which has a C implementation:

//
// File TestJava2CallingNative.java
//
class TestJava2CallingNative {
native static void sayHelloWorld();
public static void main(String args[])
  {
   String libname = args[0];
    try {
      System.loadLibrary(libname);
      System.out.println("Library " +
      libname + " successfully loaded");
     }
      catch (UnsatisfiedLinkError Err) {
      System.out.println("error: " + Err);
      return;
     }
    System.out.println("Calling sayHelloWorld");
    sayHelloWorld();
    System.out.println("All done");
    }
}

Compile this class:

$ <java_dir>/bin/javac -verbose TestJava2CallingNative.java

Output:

TestJava2CallingNative.class

Generate the JNI header file for this class. You must have the current directory in your CLASSPATH for the javah command to find your newly compiled class file.

$ <java_dir>/bin/javah -verbose -jni TestJava2CallingNative

Output:

TestJava2CallingNative.h

Here is the sample C native method implementation for sayHelloWorld:

/*
* File cImpl.c
*/
#include "TestJava2CallingNative.h"
#include <stdio.h>
JNIEXPORT void JNICALL
Java_TestJava2CallingNative_sayHelloWorld(JNIEnv *env, jclass class)
  {
   printf("C says HelloWorld via stdio\n");
  }

To compile this C source file:

$ cc -Ae +u4 +z -c -D_HPUX -D_POSIX_C_SOURCE=199506L \
-I<java_dir>/include -I<java_dir>/include/hp-ux \
cImpl.c

Output:

cImpl.o

Note that you are required to supply either -Aa or -Ae as a command line option to the C compiler. The Java header files that you must include require support for ANSI C prototypes.

Create the shared library containing the native method implementation:

PA-RISC:
$ aCC -b -o libaCCImpl.sl aCCImpl.o \
-lstd -lstream -lCsup -lm
(see below for Itanium)

Output:

libcImpl.sl
(libcImpl.so on Itanium)

To execute the Java program, you must set the SHLIB_PATH environment variable to contain the location of the directory that contains libcImpl.sl (libcImpl.so on IPF). SHLIB_PATH is the HP-UX name for LD_LIBRARY_PATH and can contain a list of directories each separated by colons.

$ export SHLIB_PATH=.:$SHLIB_PATH
$ <java_dir>/bin/java TestJava2CallingNative cImpl
Library cImpl successfully loaded
Calling sayHelloWorld
C says HelloWorld via stdio
All done

Sample Java calling native method implementation in HP aC++
Here is a sample Java program that calls a native method which has a C++ implementation. This C++ example will use the aC++ compiler. HP has two different C++ compilers, and older cfront based product and a newer aC++ compiler. You can tell which C++ compiler you are using by the name of the driver. The older cfront based product uses CC as the driver, while the newer aC++ compiler uses aCC as the name of the driver. The official HP product names for these two C++ compilers are HP C++ for the cfront based product and HP aC++ for the new C++ compiler.

(Note: In the HP-UX SDK, for the Java 2 Platform there is support for the HP aC++ compiler, but not for the older cfront HP C++ compiler. Since the HP-UX SDK, for the Java 2 Platform contains C++ libraries built with the HP aC++ compiler, any user shared libraries compiled with cfront are not compatible. Therefore, you have to recompile your shared libraries with HP aC++ if you intend to use them with the HP-UX SDK for the Java 2 Platform.)

//
// TestJava2CallingNative.java
//
class TestJava2CallingNative {


public native static void initialize();
public native static void sayHelloWorld();
public static void main(String args[]) {

String libname = args[0];

try {
System.loadLibrary(libname);
System.out.println("Library " + libname + " successfully loaded");
} catch (UnsatisfiedLinkError err) {
System.out.println("error: " + err);
return;
}

System.out.println("initialize C++ runtime");
initialize();
System.out.println("Calling sayHelloWorld");
sayHelloWorld();
System.out.println("All done!");
}

}

The code above is very similar to the code in the C-based example; the only differences are the addition of the declaration and invocation of the native method initialize(). With the PA-RISC version of HP aC++ some internal C++ runtime data structures need to be initialized before transferring control to any C++ code. The initialize() method will perform the necessary initialization. With the Itanium version of HP aC++ this initialization step is no longer needed and the initialize() method can be omitted.

Compile this class:

$ <java_dir>/bin/javac -verbose TestJava2CallingNative.java

Output:

TestJava2CallingNative.class

Generate the JNI header file for this class. You must have the current directory in your CLASSPATH for the javah command to find your newly compiled class file.

$ <java_dir>/bin/javah -verbose -jni TestJava2CallingNative

Output:

TestJava2CallingNative.h

Here is the sample C++ native method implementation for initialize and sayHelloWorld:

//
// File aCCImpl.C
//
#include "TestJava2CallingNative.h"
#include <iostream.h>
extern "C" {
void _main();
 }
JNIEXPORT void JNICALL
Java_TestJava2CallingNative_initialize(JNIEnv *, jclass)
 {
_main();
 }
JNIEXPORT void JNICALL
Java_TestJava2CallingNative_sayHelloWorld(JNIEnv *, jclass)
 {
 cout << "aC++ says HelloWorld via iostreams"
 << endl;
  }

In the above example you can see the additional native method initialize() simply calls the routine _main(). Since the Java Native interface does not allow us to call a routine name _main directly we have to write this wrapper function to allow us to call _main indirectly. Also note that we need to use initialization extern "C" to direct the C++ compiler not to perform name mangling on this routine. The entry point _main is located in the PA-RISC version of the C++ runtime support library libCsup.sl.

Note: As mentioned above the additional initialization step is no longer necessary with the Itanium version of HP aC++. In fact calling _main from code compiled by the Itanium version of HP aC++ will result in _main being an unresolved symbol.

Compile this C++ source file:

$ aCC +z +u4 -c -D_HPUX -D_POSIX_C_SOURCE=199506L
-I<java_dir>/include -I<java_dir>/include/hp-ux \
aCCImpl.C

Output:

aCCImpl.o

Create the shared library containing the native method implementation:

PA-RISC:
$ aCC -b -o libaCCImpl.sl aCCImpl.o \
-lstd -lstream -lCsup -lm
(see below for Itanium)

Output:

libaCCImpl.sl
(libaCCImpl.so on Itanium)

Note that the C++ driver program aCC must be used to create the shared library. Also note that an explicit dependency on three C++ runtime libraries need to be specified. The HP aC++ compiler has split the runtime support libraries into three separate shared libraries: libCsup.sl, libstream.sl and libstd.sl (libCsup.so, libstream.so and libstd.so on Itanium).

All HP aC++ programs require libCsup.sl (libCsup.so on Itanium). The other two libraries are required by a subset of C++ programs. In this example, we could omit libstd.sl on PA since we are not using the Standard Template Library functionality. However it is usually best to link your native method shared library against all three of these libraries.

On Itanium, create the shared library using:

$ aCC -b -o libaCCImpl.so aCCImpl.o \
-lstd_v2 -lCsup -lunwind -lm

On Itanium, -lstd_v2 is necessary, otherwise, running the program will result in UnsatisfiedLinkErrors.

Linking a shared library against these other shared libraries will cause the linker to record the fact that whenever libaCCImpl.sl (or libaCCImpl.so) is loaded into memory the dependent libraries libCsup.sl, libstream.sl and libstd.sl, (libCsup.so, libstream.so and libstd.so on Itanium) also must be loaded. This is necessary since libCsup.sl (or libCsup.so) contains the entry-point _main as well as all the other necessary runtime support routines required by every C++ method.

To execute the Java program, you must set the SHLIB_PATH environment variable to contain the location of the directory that includes libaCCImpl.sl (libaCCImpl.so on Itanium). SHLIB_PATH is the HP-UX name for LD_LIBRARY_PATH and can contain a list of directories each separated by colons.

$ export SHLIB_PATH=.:$SHLIB_PATH
$ <java_dir>/java TestJava2CallingNative aCCImpl

Library aCCImpl successfully loaded
initialize C++ runtime
Calling sayHelloWorld
ANSI C++ says HelloWorld via iostreams
All done

Native (non-Java) calling Java methods

The second capability that JNI provides is to allow non-Java to make calls into Java bytecode. This capability is provided via the C and C++ header file jni.h. Using this capability C and C++ programs can make calls into Java.

In Java 2, the Java Virtual Machine (JVM) is shipped as a shared library. To utilize the JVM in a native application you must link the application against libjvm.sl (libjvm.so on Itanium). The JNI supports an Invocation API that allows you to create and initialize a new JVM. Using this newly created JVM you can invoke various Java methods, create new Java objects or access Java objects created by the JVM.

The Invocation API is provided by the C/C++ header file jni.h. It actually provides two different interfaces. It provides a standard C interface and an object based C++ interface.

Here is the sample Java class that we will call from C and C++ programs:

//
// File TestNonJavaCallingJava2.java
//
public class TestNonJavaCallingJava2 {
    public static void printInt(int arg)
    {
        System.out.println("TestNonJavaCallingJava2.\
          printInt received: " + arg);
    }
}

Compile the above Java file into byte code:

$ <java_dir>/bin/javac -verbose TestNonJavaCallingJava2.java

Output:

TestNonCallingJava2.class

HP provides the following two examples on how to call Java from C and C++ using the standard JNI calling mechanism.

Sample code for SDK 1.3.1

Sample native calling Java implementation in C
Here is the sample C program. This program will use the JVM Invocation Interface to create a new JVM, find a class named TestNonJavaCallingJava2, and find a Java method named printInt, and invoke the method with an argument of 100.

Note:
This program works as is for SDK 1.4.2. However, if you wish to take advantage of the new JNI features in 1.4.2, in the example below you should change the line vm_args.version = JNI_VERSION_1_2; to vm_args.version = JNI_VERSION_1_4;

/*
 * File: c_main.c
 *
 * Example C Source File as the Main
 * create a new Java Virtual Machine
 * and locate the class TestNonJavaCallingJava2
 * and invoke the static method printInt
 */
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
 JNIEnv *env;
 JavaVM *jvm;
 JNIEnv jni;
 JavaVM vmi;
 JavaVMInitArgs vm_args;
 JavaVMOption options[4];
 jint res;
 printf("beginning execution...\n");
/*


* User-defined classes

options[0].optionString  = (char *) \
"-Djava.class.path=.";
vm_args.version = JNI_VERSION_1_2;
vm_args.options = options;
vm_args.nOptions = 1;
vm_args.ignoreUnrecognized = JNI_FALSE;
   /*
    * The CLASSPATH environment variable does not
    * set the classpath for JNI applications that
    * have a native main program. This behavior
    * is consistent with the JavaSoft reference platform.
    */
 /*
  * load and initialize a java vm,
  * return a JNI interface ptr in env
  */
 res = JNI_CreateJavaVM(&jvm,(void **)&env,&vm_args);
 if (res != 0) {
    printf("JNI_CreateJavaVM failed. %d\n", res);
    exit(1);
    }
 jni = *env;
 vmi = *jvm;  
  // Find the class
 jclass cls = jni->FindClass(env, "TestNonJavaCallingJava2");
 if (cls == 0) {
    printf("Could not locate class NonJavaCallingJava2");
    exit(1);
    }
    // Find the method
   jmethodID mid = jni->GetStaticMethodID(env, cls, "printInt", "(I)V");
   if (mid == 0) {
    printf("Could not locate method printInt with signature (I)V");
    exit(1);
    }
     // Invoke the method
 jni->CallStaticVoidMethod(env, cls,    mid,    100);
 // we are done
   vmi->DestroyJavaVM(jvm);
}

Compile the C source file:

$ cc -O -c -Ae +u4 -D_HPUX -D_POSIX_C_SOURCE=199506L \
-I<java_dir>/include -I<java_dir>/include/hp-ux c_main.c

Output:

c_main.o

Link the main executable.

cc -z -o c_main c_main.o \
-L<java_dir>/jre/lib/PA_RISC2.0/hotspot \
-ljvm -lpthread -llwp

On Itanium, the command to the main program would look like this:

$ cc -z -o cmain cmain.o \
  -L<java_dir>/jre/lib/IA64/hotspot \
  -ljvm -lpthread

Output:

c_main

Notes:


If you are using SDK 1.2 or 1.3 and your C main is linked EXEC_MAGIC so as to be able to address more than 1GB of private data, you must use the Hotspot JVM, or use the Classic JVM with the
"-nojit" option. The JIT compiler in Classic 1.2 and 1.3 is not compatible with EXEC_MAGIC.

To execute the C main program, set the SHLIB_PATH environment variable so the jvm libraries can be loaded.

$ export SHLIB_PATH=<java_dir>/jre/lib/PA_RISC2.0:\
<java_dir>/lib/PA_RISC2.0/hotspot
$ export CLASSPATH=.
./c_main
TestNonJavaCallingJava.printInt received: 100

To access Itanium libraries, change PA_RISC2.0 to IA64.

Sample native calling Java implementation in HP aC++
Here is the sample HP aC++ program. This program will use the C++ object-oriented interface provided in the JVM Invocation Interface to create a new JVM, find a class named TestNonJavaCallingJava2, and find a Java method named printInt, and invoke the method with an argument of 100.

Note:
This program works as is for SDK 1.4.2. However, if you wish to take advantage of the new JNI features in 1.4.2, in the example below you should change the line vm_args.version = JNI_VERSION_1_2; to vm_args.version = JNI_VERSION_1_4;

//File: aCC_main.C
//
//Example : aC++ Source File as the Main
//create a new Java Virtual Machine
//and locate the class TestNonJavaCallingJava2
//and invoke the static method printInt
#include <jni.h>
#include <iostream.h>
#include <stdlib.h>
int main()
{
  JNIEnv *env;
  JavaVM *jvm;
  JNIEnv jni;
  JavaVM vmi;
  JavaVMInitArgs vm_args;
  JavaVMOption options[4];
  jint res;
  cout << "beginning execution..." << endl;
  options[0].optionString  = (char *) \
 "-Djava.class.path=."; /* user classes */
  vm_args.version = JNI_VERSION_1_2;
  vm_args.options = options;
  vm_args.nOptions = 1;
  vm_args.ignoreUnrecognized = JNI_FALSE;
 // load and initialize a java vm, return a JNI interface ptr in env cout << "before CreateJavaVM" << endl; res = JNI_CreateJavaVM(&jvm,(void **)&env,&vm_args); if (res != 0) { cerr << "JNI_CreateJavaVM failed %d\n" << res << "." << endl; exit(1); } else cout << "after CreateJavaVM " << endl; // Find the class cout << "before FindClass" << endl; jclass cls=env->FindClass("TestNonJavaCallingJava2"); if (cls == 0) { cerr << "Could not locate class NonJavaCallingJava2 in the class path" \ << options[0].optionString << "." << endl; exit(1); } else cout << "after FindClass" << endl; // Find the method jmethodID mid=env->GetStaticMethodID(cls, "printInt", "(I)V"); if (mid == 0) { cerr << "Could not locate method printInt with signature (I)V" " in the class TestNonJavaCallingJava." << endl; exit(1); } // Invoke the method env->CallStaticVoidMethod(cls, mid, 100); // we are done jvm->DestroyJavaVM(); }
 
Compile the aC++ source file:

$ aCC -c -ext -D_HPUX -D_POSIX_C_SOURCE=199506L \
-I<java_dir>/include -I<java_dir>/include/hp-ux \
aCC_main.C

Output:

aCC_main.o

Create the main program and link:

$ aCC -z -o aCC_main aCC_main.o \
-L<java_dir>/jre/lib/PA_RISC2.0/hotspot \
-ljvm -lpthread -llwp

Notes:


If you are using SDK 1.2 or 1.3 and your C main is linked EXEC_MAGIC so as to be able to address more than 1GB of private data, you must either use Hotspot JVM or the Classic JVM with the "-nojit" option. The JIT compiler in Classic 1.2 and 1.3 is not compatible with EXEC_MAGIC.

Output:

aCC_main

To execute the aCC main program, you need to set the following environment variables so the JVM libraries can be loaded.

$ export SHLIB_PATH=<java_dir>/jre/lib/PA_RISC2.0:\
<java_dir>/lib/PA_RISC2.0/hotspot
$ export CLASSPATH=.

To access Itanium libraries change PA_RISC2.0 to IA64.

Now the program can be run:

./aCC_main
TestNonJavaCallingJava.printInt received: 100

Main/Primordial thread stack size limits

The primordial thread is the first thread when a process is created. This is the thread that has the main method. It is also called the main thread. The primordial thread stack size is controlled by the kernel parameter maxssiz or maxssiz_64bit. The Java VM (JVM) has two options for controlling the stack size:

-XX:MainThreadStackSize=n
-Xss[n][k or m]

In the Java VM the size of the primordial thread is restricted to the *greater* of MainThreadStackSize (default 2M) or ThreadStackSize (specified by -Xss). For example, if you specify -Xss1m, the JVM still takes 2M for the main thread. And if you specify -Xss4m, the JVM takes 4M for the main thread as well.

If your application calls JNI_CreateJavaVM or JNI_AttachCurrentThread from the primordial thread, under certain conditions the stack usage could cross the JVM-imposed primordial thread stack size limit of 2M and cause a stack overflow situation in the native code itself, even if no Java code was ever run on the primordial thread.

Another point to consider is the effect of maxssiz on the overall amount of writeable data space available to the application. The kernel automatically protects some memory above maxssiz away from the primordial stack base to enforce the maxssiz limit. If the maxssiz is very large, this space between the base and the protected stack top is permanently reserved for potential stack space, and it is not available for any other writeable data use such as C heap, Java heap, or other thread stacks. Therefore if you need to have the maximum amount of writeable data space available to your application, you may want to consider reducing the maxssiz. Remember, though, that maxssiz affects all applications running on the system.

Example
Consider the following example. An application calls JNI_CreateJavaVM from the primordial thread, which installs a stack overflow check at more or less 2MB above the JNI_CreateJavaVM frame. The application then makes function calls via recursion or otherwise that result in a very deep stack on the same thread independent of calling back into Java. The stack usage could cross the JVM-imposed primordial thread stack size limit of 2M and cause a stack overflow situation in the native code itself. In this case the JVM cannot take any action to resolve the problem, and the process aborts with the message:
"An irrecoverable stack overflow has occurred."

You need to distinguish stack overflow failures in the primordial thread from those occurring in other threads. If the overflow is in a Java thread, the error log generated will report the current Java thread, which can be used to identify the thread that caused the stack overflow failure. In the JVM diagnostic output, you would see a message like this:

An irrecoverable stack overflow has occurred.

An unexpected exception has been detected in native code outside the VM.
Unexpected Signal : 10 occurred at PC=0x2D10
Function=__d_trap
Library=./crjvmzon

Current Java thread:
"main" prio=7 tid=4000a1b8 nid=1 lwp_id=127233 runnable [0x00000000..0x6fff0b08]

Dynamic libraries:
./crjvmzon


text:0x00001000-0x00003db4 data:0x40001000-0x400014b4

/opt/java1.4/jre/lib/PA_RISC2.0/server/libjvm.sl


text:0xc2000000-0xc2c67000 data:0x6fe0b000-0x6ffab000

[...]

Above, you can see that the error happened on the "main" thread, that is, the one that called JNI_CreateJavaVM.

Workarounds
Here are three suggestions to work around a stack overflow problem in the main thread, listed from most desirable to least.


Use the option -XX:MainThreadStackSize=<value> to increase the primordial thread stack size, However be aware that -XX options are non-standard options, and are liable to change from release to release.

Use the -Xss option to set the thread stack size. This workaround may be less desirable because -Xss sets the stack size for all threads, not just the primordial thread. For example, if your application has 100 threads and you set the maximum thread stack size to 4M with the -Xss option, the application would use nearly 400MB of address space just for thread stacks. If you did not use the -Xss option, the application would have only used 50MB (512k (the default -Xss value) * 100 threads) of address and swap space.

Change the application so as to Call JNI_CreateJavaVM() from a non primordial thread. That is, create a new thread with an appropriate stack size, using pthread_create() and use this new thread to initialise the JVM.

Non-Main/Primordial thread stack size limits

The default stack size for 1.4 64-bit mode threads created by the JVM is 1MB. On PA-RISC 32 and 64-bit systems, the default stack size for threads created using the pthread library is 64KB.

Here are some suggestions to work around stack overflow problems in threads other than the main thread:


If the thread is created in native code and is attached to Java through JNI_AttachCurrentThread, increase the stack size attribute when creating the thread with pthread_create.

If the thread is created inside Java and there is a stack overflow condition, increase the thread stack size with -Xss<n>

Dynamically loading the Hotspot JVM (SDK 1.3.1.x and 1.4.2.x PA-RISC)

Normally you would link against the JRE and use HotSpot, but sometimes you might want to load the JVM dynamically, especially if you are integrating Java with legacy applications.

(The example for Dynamically loading the Classic JRE can now be found in the Appendix.)

Note: The examples below work as is for Java 1.3.1.x and 1.4.2.x on PA-RISC. If you wish to take advantage of the new JNI features in 1.4.2, in the example below you should change the line vm_args.version = JNI_VERSION_1_2; to vm_args.version = JNI_VERSION_1_4;

Below is an example of how to dynamically load the shared library libjvm.sl, (libjvm.so for Itanium), create a new JVM, locate the class TestNonJavaCallingJava2, and invoke the static method printInt.


#include <jni.h> #include <stdlib.h> #include <dl.h> typedef int (*JNI_CreateJavaVM_t)(JavaVM**, void**, void*); int main() { JNIEnv *env; JavaVM *jvm; JNIEnv jni; JavaVM vmi; JavaVMInitArgs vm_args; JavaVMOption options[4]; jint res; printf ("beginning execution...\n"); options[0].optionString = (char *) "-Djava.class.path=."; /* user classes */ vm_args.version = JNI_VERSION_1_2; vm_args.options = options; vm_args.nOptions = 1; vm_args.ignoreUnrecognized = JNI_FALSE; // load libjvm.sl (PA-RISC) or libjvm.so (Itanium) shl_t libsym = shl_load("libjvm.sl", BIND_IMMEDIATE|DYNAMIC_PATH, 0L); if (libsym == 0) { fprintf(stderr, "Unable to shl_load libjvm.sl\n"); exit (1); } else fprintf(stdout, "shl_loaded libjvm.sl successfully!\n"); // find function JNI_CreateJavaVM JNI_CreateJavaVM_t func = NULL; res = shl_findsym(&libsym, "JNI_CreateJavaVM", TYPE_PROCEDURE, &func); if (res == 0) fprintf(stdout, "Found symbol JNI_CreateJavaVM!\n"); else { fprintf(stderr, "Unable to find symbol JNI_CreateJavaVM!\n"); exit (1); } // load and init a java vm, return a JNI interface ptr in env printf ("before CreateJavaVM\n"); res = (*func)(&jvm, (void **)&env, (void *)&vm_args); if (res != 0) { fprintf (stderr, "JNI_CreateJavaVM failed. %d\n", res); exit (1); } else fprintf (stdout, "after CreateJavaVM \n"); jni = *env; vmi = *jvm; // find the class fprintf (stdout, "before FindClass\n"); jclass cls=env->FindClass("TestNonJavaCallingJava2"); if (cls == 0) { fprintf (stderr, "Could not locate class TestNonJavaCallingJava2 in classpath %s\n", options[0].optionString); exit(1); } else fprintf (stdout, "after FindClass\n"); // find the method jmethodID mid=env->GetStaticMethodID(cls, "printInt", "(I)V"); if (mid == 0) { fprintf (stderr, "Could not locate method printInt with signature (I)V in the class TestNonJavaCallingJava."); exit(1); } else fprintf (stdout, "after GetStaticMethod\n"); fflush (stdout); // invoke the method env->CallStaticVoidMethod(cls, mid, 100); // we are done jvm->DestroyJavaVM(); } To compile the program: aCC --c loadjava.cpp -mt -ext -D_HPUX -D_POSIX_C_SOURCE=199506L \ -I<java_dir>/include -I<java_dir>/include/hp-ux Linking the program: To link the program on PA-RISC: aCC -z -mt -o loadjava loadjava.o -lpthread To link the program on Itanium: aCC -z -mt -o loadjava loadjava.o -lpthread # allow use of SHLIB_PATH to locate shared libraries required by program chatr +s enable loadjava To execute the program, set environment variables. The example is for PA-RISC. For Itanium SDK 1.3.1.x, substitute IA64 for PA_RISC2.0 in the command below. For Itanium SDK 1.4.2.x substitute IA64N for PA_RISC2.0 in the command below. export SHLIB_PATH=$JAVA_HOME/jre/lib/PA_RISC2.0/hotspot:$JAVA_HOME/jre/lib/PA_RISC2.0 export CLASSPATH=. (Note: we are using the same TestNonJavaCallingJava2.class from prior examples) # Run program # Use LD_PRELOAD_ONCE if want to dynamically load library with TLS (e.g. HotSpot). For Itanium, substitute IA64 or IA64N for PA_RIS2.0, and substitute libjvm.so for libjvm.sl LD_PRELOAD_ONCE=$JAVA_HOME/jre/lib/PA_RISC2.0/hotspot/libjvm.sl ./loadjava # Output: #TestNonJavaCallingJava.printInt received: 100 

Additional references

Java Native Interface: Programmer's Guide and Specification by Sheng Liang

Please let us know additional information you'd like to see in the programmer's guide.

 

Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. Hewlett-Packard is independent of Sun Microsystems, Inc.


 

'Java' 카테고리의 다른 글

JDK and TimeZone  (0) 2006.12.07
여러종류의 데이타베이스 테스트 소스  (0) 2006.11.07
EXCEPTION_ACCESS_VIOLATION [Java Exception]  (0) 2006.11.01