JDK-4712793 : JNI : Failure in JNI_CreateJavaVM() after calling DestroyJavaVM()
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 1.3.1,1.4.0
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_98,windows_nt,windows_2000
  • CPU: x86
  • Submitted: 2002-07-10
  • Updated: 2012-11-02
  • Resolved: 2005-09-21
Description
Name: gm110360			Date: 07/10/2002


FULL PRODUCT VERSION :
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
In order to give C++ access to some Java code, we provide
an intermediate class that :
- creates a JVM (JNI_CreateJavaVM()),
- passes appropriate arguments to Java classes
- calls DestroyJavaVM() to destroy the JavaVM.

All goes well the first time, but when trying to call the
function again from the same application, we get the as
listed in the Error Messages section.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
#
# HotSpot Virtual Machine Error, Internal Error
# Please report this error at
# http://java.sun.com/cgi-bin/bugreport.cgi
#
# Java VM: Java HotSpot(TM) Client VM (1.4.0-b92 interpreted mode)
#
# Error ID: 455843455054494F4E530E43505000DF
#
# Problematic Thread: prio=183470880 tid=0x0AEF8B20 nid=0x1e0700 runnable
#

This bug can be reproduced always.

---------- BEGIN SOURCE ----------
C++ source code for function

int CALLBACK compareImages(const char ** compareString, string& results, int&
results_count) {
	JavaVMOption options[4];
	JavaVMInitArgs vm_args;
	JavaVM *jvm = NULL;
	JNIEnv *env;
	long result = 0;
	jmethodID mid;
	jclass jcls;
	_jobjectArray *compareResult;
	jsize rSize;
	
	//Set JVM options
	options[0].optionString = "-Djava.class.path=.";
	options[1].optionString = "-Djava.compiler=NONE";
	options[2].optionString = "-Djava.library.path=.";
	options[3].optionString = "-verbose:jni";

	vm_args.version = JNI_VERSION_1_2;

	vm_args.options = options;
	vm_args.nOptions = 4;
	vm_args.ignoreUnrecognized = JNI_FALSE;

	//Create the JVM
	result = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);

	if (result == JNI_ERR) {
		results = "Error invoking the JVM\n";
		jvm->DestroyJavaVM();
		return -1;
	}

	//Find class IksneWrapper
	jcls = env->FindClass("IksneWrapper");
	if (jcls == 0 || jcls == NULL) {
		results = "Cannot find class IksneWrapper\n";
		jvm->DestroyJavaVM();
		return -1;
	}

	env->ExceptionClear();

	//Find public static String[] compare(String compareString);
	mid = env->GetStaticMethodID(jcls, "compare", "(Ljava/lang/String;)
[Ljava/lang/String;");

	if (mid == 0) {
		results = "Cannot find IksneWrapper.compare function\n";
		jvm->DestroyJavaVM();
        return -1;
    }

	//Call method and compare
	compareResult = (jobjectArray)((jarray) env->CallStaticObjectMethod
(jcls, mid, env->NewStringUTF(*compareString)));
	rSize = env->GetArrayLength(compareResult);
	
	// If an error occured, only one result is returned with error message
	if (rSize == 1) {
		results = env->GetStringUTFChars(
			(jstring) env->GetObjectArrayElement(compareResult, 0),
JNI_FALSE);
		results_count = 0;
		jvm->DestroyJavaVM();
		return 0;
	}
	// If only one test was executed, only test result is returned
	else if (rSize == 2) {
		results = env->GetStringUTFChars(
			(jstring) env->GetObjectArrayElement(compareResult, 1),
JNI_FALSE);
		results_count = 1;
	}
	// Test results of all executed tests are returned
	else {
		int i;
		results = "";
		for (i=1; i < rSize; i++) {
			results = results.append(env->GetStringUTFChars
((jstring) env->GetObjectArrayElement(compareResult, i), JNI_FALSE));
			if (i != (rSize - 1))
				results = results.append("\n");
		}
		results_count = rSize - 1;
	}

	//delete [] vmBuf;
	jvm->DetachCurrentThread();
	jvm->DestroyJavaVM();
	return 1;
}

--------------------------------------------------------------------------------

C++ Test application

#include "windows.h"

#include <iostream>
#include <string>
//#include <stdio.h>

using namespace std;

typedef int (CALLBACK* PCOMPIMAGE) (const char**, string&, int&);

int func() {
	HINSTANCE hDLL;        // Handle to DLL
	PCOMPIMAGE pComImg;    // Function pointer

	/*sample compare string call of format :
		<base image> <test image> <comparison command>
	 */
	const char *compareString
= "\\\\jayant\\testImages\\testColors\\blank.bmp
\\\\jayant\\testImages\\testColors\\bmp.bmp COMPARE_PIXELBYPIXEL true 0
OPERATE_COMPAREBYRGB COMPARE_PIXELBYPIXEL true 25";
	//string compareString = "\\\\jayant\\testImages\\testColors\\blank.bmp
\\\\jayant\\testImages\\testColors\\bmp.bmp COMPARE_PIXELBYPIXEL true 0
OPERATE_COMPAREBYRGB COMPARE_PIXELBYPIXEL true 25";
	string result;
	int i; //number

	hDLL = LoadLibrary("IksneCpp");
	if (hDLL != NULL)
	{
		pComImg = (PCOMPIMAGE) GetProcAddress(hDLL,"compareImages");
		if (!pComImg)
		{
			// handle the error
			FreeLibrary(hDLL);
			return -1;
		}
		else
		{
			// call the function
			if (pComImg(&compareString, result, i) == 1) {
				//printf("For %s,\ntotal results = %d,\nresult
= %s\n", compareString, i, result);
				cout << "For " << compareString << endl
<< "total results = " << i << endl;
				cout << "(\\n seperated results)" << endl <<
result << endl;
			}
			else
				cout << "Error : " << result << endl;
				//printf("Error : '%s'\n", result);
		}
	}

	FreeLibrary(hDLL);
	
	hDLL = LoadLibrary("IksneCpp");
	if (hDLL != NULL)
	{
		pComImg = (PCOMPIMAGE) GetProcAddress(hDLL,"compareImages");
		if (!pComImg)
		{
			// handle the error
			FreeLibrary(hDLL);
			return -1;
		}
		else
		{
			// call the function
			if (pComImg(&compareString, result, i) == 1) {
				//printf("For %s,\ntotal results = %d,\nresult
= %s\n", compareString, i, result);
				cout << "For " << compareString << endl
<< "total results = " << i << endl;
				cout << "(\\n seperated results)" << endl <<
result << endl;
			}
			else
				cout << "Error : " << result << endl;
				//printf("Error : '%s'\n", result);
		}
	}

	FreeLibrary(hDLL);
	GlobalFree((void *) compareString);

	return 0;
}

int main()
{
	int res;
	res = func();
	res = func();
	return 0;
}

---------- END SOURCE ----------
(Review ID: 147214) 
======================================================================

Name: rmT116609			Date: 10/10/2002


FULL PRODUCT VERSION :
java version "1.4.0_02"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0_02-b02)
Java HotSpot(TM) Client VM (build 1.4.0_02-b02, mixed mode)

FULL OPERATING SYSTEM VERSION :
Windows NT Version 4.0 Service Pack 6a Numerous hot fixes for security issues


A DESCRIPTION OF THE PROBLEM :
The environment is a C++ application using the Invocation API to launch a Java VM.

The C++ code does the following (in pseudocode):

=======On Initialization========
JNI_CreateJavaVM()        <<-- crashes here 2nd time
FindClass()/GetMethodID() for classes used
Create NewGlobalRef() for classIDs

=======On Shutdown========
DeleteGlobalRef for classID references
DetachCurrentThread
DestroyJavaVM <<-- returns 0!

Our code repeatedly attempts to shutdown and restart. The
second restart, the code crashes.
Here is the output from stderr when the JVM crashes:
#
# HotSpot Virtual Machine Error, Internal Error
# Please report this error at
# http://java.sun.com/cgi-bin/bugreport.cgi
#
# Java VM: Java HotSpot(TM) Client VM (1.4.0_02-b02 mixed
mode)
#
# Error ID: 455843455054494F4E530E43505000DF
#
# Problematic Thread: prio=10821120 tid=0x00A51E00 nid=0xf1
runnable
#

EXPECTED VERSUS ACTUAL BEHAVIOR :
The JNI_CreateJavaVM function should succeed (or barring that, fail with a return code).


REPRODUCIBILITY :
This bug can be reproduced always.

CUSTOMER WORKAROUND :
Comment out the DestroyJavaVM call.
This however leads to a memory leak that causes the application to crash after several days running.
(Review ID: 165185)
======================================================================

Comments
EVALUATION From the Java SE 6 JNI Spec: http://download.java.net/jdk6/docs/technotes/guides/jni/spec/invocation.html#wp15956 "JNI_CreateJavaVM jint JNI_CreateJavaVM(JavaVM **p_vm, JNIEnv **p_env, void *vm_args); Loads and initializes a Java VM. The current thread becomes the main thread. Sets the env argument to the JNI interface pointer of the main thread. As of JDK/JRE 1.2 , creation of multiple VMs in a single process is not supported." The last line implies that that once a VM is created and destroyed, one cannot start another VM. A simple test case was used to reproduce the problem as specified in the pseudo-code, and the JNI_CreateJavaVM correctly returns !JNI_OK. If a reproducible test case is provided please submit another bug and refer to this one at http://bugs.sun.com/services/bugreport/index.jsp The restartable VMs issue is separately tracked under 6179880.
18-10-2006

EVALUATION I don't believe this was ever supported. The documentation says that you can't create multiple VM's in a single process, and doesn't say that you can't recreate a VM in a process, but I think the problems are the same. There's a comment in the code: // At the moment it's only possible to have one Java VM, // since some of the runtime state is in global variables. That said the VM shouldn't give an internal error which comes from ExceptionMark not expecting a pending exception fatal("ExceptionMark constructor expects no pending exceptions"); so this should be investigated. I haven't been able to get the sample program to the point of debugging yet, but my simple program gets a JNI_ERR for this. This support cannot be added for hopper, and will be reevaluated for mantis. ###@###.### 2002-07-12
12-07-2002

WORK AROUND Rearrange code to not recreate the VM in the same process. ###@###.### 2002-07-12
12-07-2002