Proper termination of JVM

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Good old Grim
    New Member
    • Feb 2008
    • 7

    Proper termination of JVM

    I'm not sure which section I should post this in, since the problem covers Java, C++ and .NET managed C++...

    Anyway - I'm using some Java classes via JNI in unmanaged C++, then call the unmanaged C++ class from managed C++.

    What I want to be able to do is create an object that uses JNI, then destroy it, then create and use another.

    The problem is that the second time around funcCreateJavaV M returns -1 (unknown error). I figure I'm not disposing it properly the first time, but I'm not sure what I'm doing wrong. The call to DestroyJavaVM is in the destructor of the unmanaged class.

    The unmanaged class looks something like this:

    [CODE=cpp]//Path to jvm.dll
    #define JVMDLLPATH "C:\\Progra m Files\\Java\\jd k1.6.0_04\\jre\ \bin\\client\\j vm.dll"
    //Function type for JNI_CreateJavaV M
    typedef UINT (CALLBACK* CreateJavaVM)(J avaVM**,void**, JavaVMInitArgs* );

    class edoc
    {
    JNIEnv *env;
    JavaVM *jvm;

    HINSTANCE jvmdll;
    JavaVMInitArgs vmargs;

    public:

    void init()
    {
    env = (JNIEnv*)malloc (sizeof(JNIEnv) );
    jvm = (JavaVM*)malloc (sizeof(JavaVM) );

    //Load jvm.dll
    jvmdll = LoadLibrary(JVM DLLPATH);

    //Initialize function variable for JNI_CreateJavaV M
    CreateJavaVM funcCreateJavaV M;
    funcCreateJavaV M = (CreateJavaVM)G etProcAddress(j vmdll,"JNI_Crea teJavaVM");

    //Start JVM
    JavaVMOption vmoptions[1];
    vmargs.version= JNI_VERSION_1_6 ;
    vmargs.nOptions =2;
    vmoptions[0].optionString=V MOPTIONCLASSPAT H; //capitalized are macros containing paths
    vmoptions[1].optionString=V MOPTIONPATH;
    vmargs.options = vmoptions;
    vmargs.ignoreUn recognized = JNI_FALSE;
    funcCreateJavaV M(&jvm, (void**)&env, &vmargs);

    //multiple calls to JVM via env go here - initialising jclass, jmethodID variables and the like
    }
    ~edoc()
    {
    jvm->DestroyJavaVM( );
    FreeLibrary(jvm dll);
    }
    };

    The managed class looks something like this:
    public ref class clrEDOC
    {
    edoc* ed;

    public: clrEDOC()
    {
    ed = new edoc();
    ed->init();
    }
    ~clrEDOC()
    {
    ed->~edoc(); //used to be "delete ed;"
    }
    };

    And the code that calls it is as follows (edocjni is the namespace):
    void main()
    {
    edocjni::clrEDO C^ clred = gcnew edocjni::clrEDO C(); //executes properly
    clred->~clrEDOC(); //used to be "delete clred;"
    edocjni::clrEDO C^ ed = gcnew edocjni::clrEDO C(); //unknown error while creating JVM
    }[/CODE]
    Last edited by Ganon11; Feb 15 '08, 02:47 PM. Reason: Please use the [CODE] tags provided.
  • Good old Grim
    New Member
    • Feb 2008
    • 7

    #2
    I may have found a solution myself, but AFAIK its undocumented and I have no idea how good it will work, but the first tests are successful.

    What I learned was - the call to DestroyJavaVM does not actually destroy JVM. Quoting from Sun "The VM waits until the main thread is the only user thread before it actually unloads." Why does it not happen immediately, if I have not attached any extra threads to it, I have no idea.

    Anyway, until JVM is properly destroyed, another one cannot start in the same thread (hence the error I was getting).

    So what I do now, is save the pointer to JNIEnv and submit it as an argument to the function initializing JVM. The beginning of the init method now looks like this:

    void init(void* _env)
    {
    if (_env == NULL) env = new JNIEnv; else env = (JNIEnv*)_env;

    I also added a void* getenv() method to get the pointer before disposing the object and removed the DestroyJavaVM call.

    Initialized another object and the calls to JVM via the submitted pointer are successful.

    Any input on the matter is still appreciated.

    Comment

    • Good old Grim
      New Member
      • Feb 2008
      • 7

      #3
      For the record: the way I ultimately did that was like this:

      Code:
      int i = this->getCreatedJVMs();
        if (i>0)
          this->attachtoJVM();
        else
          this->launchJVM();
      where launchJVM() launches JVM as shown in my first post, attachtoJVM() looks like this:
      Code:
      	void attachtoJVM()
      	{
      		dllpath = JVMDLLPATH;
      		jvmdll = LoadLibrary(dllpath);
      		GetCreatedJavaVMs funcGetCreatedJavaVMs;
      		funcGetCreatedJavaVMs = (GetCreatedJavaVMs)GetProcAddress(jvmdll,"JNI_GetCreatedJavaVMs");
      		JavaVM **javm;
      		int buflen = 4;
      		int *nVMs;
      		int i = funcGetCreatedJavaVMs(javm, buflen, nVMs);
      		javm[0]->AttachCurrentThread((void**)&env,NULL);
      	}
      And getCreatedJVMs( ) looks like this:
      Code:
      	//Returns a positive value if at least one JVM is running. 
      	//This value can be either the number of JVMs running or the memory address of one of them
      	//Behaviour not fully documented.
      	int getCreatedJVMs()
      	{	dllpath = JVMDLLPATH;
      		jvmdll = LoadLibrary(dllpath);
      		GetCreatedJavaVMs funcGetCreatedJavaVMs;
      		funcGetCreatedJavaVMs = (GetCreatedJavaVMs)GetProcAddress(jvmdll,"JNI_GetCreatedJavaVMs");
      		JavaVM **javm;
      		int buflen = 4;
      		int *nVMs;
      		int i = funcGetCreatedJavaVMs(javm, buflen, nVMs);
      		if (nVMs==0) return 0; else return (*nVMs);
      	}
      This requires another typedef to work:
      Code:
      typedef UINT (CALLBACK* GetCreatedJavaVMs)( JavaVM**, int, int* );

      Comment

      • saikrishnatudur
        New Member
        • Jun 2014
        • 1

        #4
        Thank You..!

        Thank You..!
        It worked great for me..

        You saved my time.

        Comment

        Working...