- PR -

JNIで困っています。

1
投稿者投稿内容
かしん
常連さん
会議室デビュー日: 2004/08/27
投稿数: 25
お住まい・勤務地: 窓際
投稿日時: 2004-12-27 22:40
いつもお世話になっております。

JavaVMをロードしてSystem#currentTimeMillisを呼び出すSOを、
java側から呼び出す処理を作成してます。

libChild.so (JavaVMをロード、SystemクラスのcurrentTimeMillisメソッドを呼び出す)
libParent.so (libChildを呼び出すJNI)
LibTest.java (libParentを呼び出す)

上記を実行すると、libChild.so内でSystemクラスをfindする際に落ちてしまいます。

しかし呼び出し元をCのメインに変更すると正常に終了します。
libChild.so (JavaVMをロード、SystemクラスのcurrentTimeMillisメソッドを呼び出す)
libParent.so (libChildを呼び出すJNI)
libDebug.c (libParanetを呼び出す)

どちらともLD_LIBRARY_PATHの設定は同じ。

初心者なので何が原因なのかわかりません。
このようなことは出来ない仕様なのでしょうか。

/*---- libChild.c ----*/
#include "libChild.h"

#include <jni.h>
#include <stdio.h>

int testChild() {
JavaVM* vm;
JNIEnv* env;
JavaVMInitArgs initArgs;
initArgs.version = 0x00010002;

JNI_GetDefaultJavaVMInitArgs(&initArgs);
JNI_CreateJavaVM(&vm, &env, &initArgs);
if((*vm) == NULL){return -1;}
if((*env) == NULL){return -1;}

jclass cls = (*env)->FindClass(env, "Ljava/lang/System;");
if(cls == NULL){return -1;}

jmethodID mid = (*env)->GetStaticMethodID(env, cls, "currentTimeMillis", "()J");
if(mid == NULL){return -1;}

(*env)->CallStaticLongMethod(env, cls, mid);

return 0;
}

/*---- libParent.c ----*/
#include "libParent.h"
#include "libChild.h"
#include "test_libTest_LibTest.h" // JNI head

void testParent(){
int ret = testChild();
}

void Java_test_libTest_LibTest_testParent (JNIEnv* env, jobject obj){
testParent();
jclass cls = (*env)->FindClass(env, "Ljava/lang/System;");
if(cls == NULL){return;}

jmethodID mid = (*env)->GetStaticMethodID(env, cls, "currentTimeMillis", "()J");
if(mid == NULL){return;}

(*env)->CallStaticLongMethod(env, cls, mid);
}

/*---- LibTest.java ----*/
package test.libTest;
public class LibTest {

native public void testParent();

public static void main(String[] args){
LibTest test = new LibTest();
synchronized(test){
System.loadLibrary("Parent");
test.testParent();
test = null;
}
}
}
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2004-12-28 05:01
unibon です。こんにちわ。

引用:

かしんさんの書き込み (2004-12-27 22:40) より:
libChild.so (JavaVMをロード、SystemクラスのcurrentTimeMillisメソッドを呼び出す)
libParent.so (libChildを呼び出すJNI)
LibTest.java (libParentを呼び出す)

上記を実行すると、libChild.so内でSystemクラスをfindする際に落ちてしまいます。



あまり良くは知りませんが、Java(A)→native→Java(B) のような呼び出しですよね。ちなみに、最初のJava(A)と最後のJava(B)は、必ずしも同じ Java VM のインスタンスになるとは限らないんですよね?
なんだか複雑すぎてうまく動かないのも分かる感じもします。 でも、原理的には動いても良さそうですよね。ちなみに「落ちてしまいます」とありますが、どんな落ち方(メモリアクセス違反, etc.)なのでしょうか?

原因をかなり絞り込まれているようなので、再現できるコードを全行に渡って提示されたほうが他の方が試しやすくて手っ取り早いかもしれません。


#以下、あとで追加。
上記で「インスタンス」と書きましたが、そうじゃなくて、たとえば、Java(A) が Java2 バージョン 1.3 で、Java(B) が Java2 バージョン 1.2 みたいなことです。

[ メッセージ編集済み 編集者: unibon 編集日時 2004-12-28 05:04 ]
さくらば
大ベテラン
会議室デビュー日: 2002/11/12
投稿数: 145
投稿日時: 2004-12-28 10:51
こんにちは、さくらばです。

引用:

unibonさんの書き込み (2004-12-28 05:01) より:

あまり良くは知りませんが、Java(A)→native→Java(B) のような呼び出しですよね。ちなみに、最初のJava(A)と最後のJava(B)は、必ずしも同じ Java VM のインスタンスになるとは限らないんですよね?
なんだか複雑すぎてうまく動かないのも分かる感じもします。 でも、原理的には動いても良さそうですよね。ちなみに「落ちてしまいます」とありますが、どんな落ち方(メモリアクセス違反, etc.)なのでしょうか?




今のところ Java は 1 つのプロセスで、1 つの VM しかサポートしていないので、
JNI_CreateJavaVM で新たに VM を立ち上げようとしてもできません。

VM が立ち上がらない場合 JNI_CreateJavaVM の戻り値が負の値になっているで、
それで確認できます。

もし、同じプロセスの VM にアタッチしたいというのであれば、AttachCurrentThread
メソッドを使用すればいいと思います。
Gio
ぬし
会議室デビュー日: 2003/11/28
投稿数: 350
お住まい・勤務地: 都内から横浜の間に少量発生中
投稿日時: 2004-12-28 11:50
動作を確認しようとしてヘッダを勝手に拵えたりしておりましたが、さくらばさんからフォローがあったので確認コードを突っ込んでみました。
ちなみに、こちらで確認した環境は以下の通りです。
全面的に古いですがご了承ください。

OS: RedHat Linux 2.4
gcc: 2.96
jdk: 1.3.1_15

ヘッダファイル libParent.h と libChild.h が示されていませんが、必要最小限の extern 宣言だけを含んだものを使いました。
(コンパイルを通すだけのものです)

結果、testChild() 関数中の JNI_CreateJavaVM がエラーコードを返しています。
引数に与えた vm や env は NULL チェックを通過してしまうので、結果として FindClass() 関数で初めてエラーが露見した恰好になっているようです。

ちなみに、libParent ファイル中に testChild() を呼ぶだけの main() 関数を作成してやると、この場合はさくらばさんが書かれたとおり既存 VM がないので JNI CreateJavaVM が成功し、エラーなしに通りました。
かしん
常連さん
会議室デビュー日: 2004/08/27
投稿数: 25
お住まい・勤務地: 窓際
投稿日時: 2004-12-28 15:51
返答が遅くなりました。
unibonさん、さくらばさん、Gioさん、回答ありがとうございます。

libParent.h、libChild.hについては、コンパイルを通すためのものだと思ってください。

SunのJNIのAttachCurrentThreadの説明に、しっかりと「ネイティブスレッドを 2 つの Java VM へ同時に接続することはできません。」と書いてありました。(^^;
libChild側で、対応することで、正常に動作を行うことが出来ました。
ご協力ありがとうございました。

/*---- libChild.c 改 ----*/
#include "libChild.h"
#include <jni.h>
#include <stdio.h>

int testChild() {
JavaVM* vm;
JNIEnv* env;
JavaVMInitArgs initArgs;

jboolean flag = JNI_FALSE;

initArgs.version = 0x00010002;
jint ret = JNI_GetDefaultJavaVMInitArgs(&initArgs);

ret = JNI_CreateJavaVM(&vm, (void**)&env, (void*)&initArgs);
if(ret != 0){
int vmn;
ret = JNI_GetCreatedJavaVMs(&vm, 1, &vmn);
if(ret != 0 || vmn < 1){return -1;}
ret = (*vm)->AttachCurrentThread(vm, (void**)&env, (void*)&initArgs);
if(ret != 0){return -1;}
flag = JNI_TRUE;
}

jclass cls = (*env)->FindClass(env, "Ljava/lang/System;");
if(cls == NULL){return -1;}

jmethodID mid = (*env)->GetStaticMethodID(env, cls, "currentTimeMillis", "()J");
if(mid == NULL){return -1;}

(*env)->CallStaticLongMethod(env, cls, mid);

if(flag == JNI_FALSE){
(*vm)->DestroyJavaVM(vm);
}
return 0;
}
1

スキルアップ/キャリアアップ(JOB@IT)