Problem: Initializing a Java VM "steals" stack space from other
threads. It basically renders JNI unusable in any non-trivial way. The
problem occurs on Solaris 8/10 on either sparc/x86. A test program is
included to demonstrate the problem.
Java Version:
>> java -version java version "1.5.0_02" Java(TM) 2 Runtime Environment,
Standard Edition (build 1.5.0_02-b09) Java HotSpot(TM) Server VM (build
1.5.0_02-b09, mixed mode)
This program demonstrates that initializing a Java VM "steals"
* stack space from other threads.
* For instance, in the sample output below, before initializing the
* JVM, the application has used up 1.8M of stack space. But after
* initializing the JVM, the main thread's allocated stack space has
* shrunk to just the single 488K block at 0xFFB76000! That means
* that the main thread can never again exceed 488K of stack, or else
* the program will crash.
Stack starts near 0xffbeef10
Initial stack:
FF3A0000 176K read/exec /usr/lib/ld.so.1
FF3DC000 8K read/write/exec /usr/lib/ld.so.1
FF3DE000 8K read/write/exec /usr/lib/ld.so.1
FFBEC000 16K read/write/exec [ stack ]
total 7352K
recurse() stack near 0xffa1a2ec
Stack after recurse(20000):
FF3A0000 176K read/exec /usr/lib/ld.so.1
FF3DC000 8K read/write/exec /usr/lib/ld.so.1
FF3DE000 8K read/write/exec /usr/lib/ld.so.1
FFA18000 1888K read/write/exec [ stack ]
total 9224K
Stack after jvm:
FF3A0000 176K read/exec /usr/lib/ld.so.1
FF3DC000 8K read/write/exec /usr/lib/ld.so.1
FF3DE000 8K read/write/exec /usr/lib/ld.so.1
FFA18000 1376K read/write/exec [ stack ]
FFB70000 24K - [ stack ]
FFB76000 488K read/write/exec [ stack ]
total 82304K
To compile:
solaris sparc:
cc -g -I/usr/local/java/jdk/include
-I/usr/local/java/jdk/include/solaris
-L/usr/local/java/jdk/jre/lib/sparc -R/usr/local/java/jdk/jre/lib/sparc
-ljvm -lpthread stack.c
solaris x86:
cc -g -I/usr/local/java/jdk/include
-I/usr/local/java/jdk/include/solaris -L/usr/local/java/jdk/jre/lib/i386
-R/usr/local/java/jdk/jre/lib/i386 -ljvm -lpthread stack.c
linux:
gcc -g -I/usr/local/java/jdk/include
-I/usr/local/java/jdk/include/linux
-L/usr/local/java/jdk/jre/lib/i386/server
-Wl,-rpath,/usr/local/java/jdk/jre/lib/i386/server -ljvm -lpthread stack.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <jni.h>
void recurse(int n)
{
if (n > 0) {
recurse(n-1);
} else {
printf("recurse() stack near 0x%x\n", &n);
}
}
void pmap()
{
/* check if we have gnu grep installed */
int ret = system("echo hi | gnu grep -1 hi > /dev/null 2>&1");
char buf[BUFSIZ];
if (ret == 0) {
snprintf(buf, sizeof(buf), "pmap %d | gnu grep -3 stack", getpid());
} else {
snprintf(buf, sizeof(buf), "pmap %d | grep stack", getpid());
}
system(buf);
}
void jvm()
{
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_4;
vm_args.nOptions = 0;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
JavaVM *jvm;
JNIEnv *env;
long result = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);
if (result == JNI_ERR) {
fprintf(stderr, "ERROR initializing the JVM\n");
exit(1);
}
}
int main(int argc, char **argv)
{
int x;
printf("Stack starts near 0x%x\n", &x);
printf("\nInitial stack:\n");
pmap();
printf("\n");
int n = 20000;
#ifdef __linux
/* linux appears to give you a larger initial stack */
n = 100000;
#endif
if (argc >= 2) {
n = atoi(argv[1]);
}
recurse(n);
printf("\nStack after recurse(%d):\n", n);
pmap();
jvm();
printf("\nStack after jvm:\n");
pmap();
printf("\nPrepare to crash:\n");
recurse(n*2);
printf("\nWOOHOO! No crash!\n");
}
###@###.### 2005-05-12 19:57:37 GMT