United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-6269555 : creating JVM via C program "steals" space from main thread stack rendering JNI useless

Details
Type:
Bug
Submit Date:
2005-05-12
Status:
Closed
Updated Date:
2013-06-21
Project Name:
JDK
Resolved Date:
2005-07-13
Component:
hotspot
OS:
solaris_8
Sub-Component:
runtime
CPU:
generic
Priority:
P2
Resolution:
Fixed
Affected Versions:
5.0u2
Fixed Versions:

Related Reports
Backport:
Backport:
Relates:
Relates:

Sub Tasks

Description
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

                                    

Comments
EVALUATION

See comments.

###@###.### 2005-06-01 13:58:03 GMT
                                     
2005-06-01
WORK AROUND

Use -Xss<stacksize> set to the same value as "limit" reports.

e.g.
$ limit
cputime         unlimited
filesize        unlimited
datasize        unlimited
stacksize       8192 kbytes
coredumpsize    unlimited
vmemoryuse      unlimited
descriptors     1024

Pass "-Xss8192k" as a member of JavaVMOption array to JNI_CreateJavaVM.  If stacksize is unlimited, just set -Xss to an appropriately large value.  NOTE that this setting will be used for stacksize for ALL Java threads so it may not be appropriate depending on the application.  But it should avoid the crash in the testcase on Solaris.

Linux only: Initial thread has a hard limit of 2m (4m for 64-bit VM) due to limitations of older glibc/kernel combination.  Due to this, the above workaround will be less effective but may still give some relief.

###@###.### 2005-06-02 19:21:32 GMT

This workaround isn't working on my linux-suse-9.2. -Xss set stack size for all threads except main process thread. Maximum size of stack for main thread is 8mb on linux. Running jvm reduce this value to 2mb.
###@###.### 2005-06-03 07:53:22 GMT

Another possible work around is to avoid calling JNI_CreateJavaVM from the primordial thread.  It is easier for the VM to detect the stack size of non-primordial thread and should avoid creating guard pages in the middle of the stack.

###@###.### 2005-06-06 19:32:48 GMT
                                     
2005-06-02



Hardware and Software, Engineered to Work Together