私は JVMTI エージェントとして消しゴムのデータ競合検出アルゴリズムの実装に取り組んでいます。サンプル入力を実行してコードをテストしようとすると、JVM がクラッシュし、次のようなダンプが表示されます (同じエラーの他のスタック トレースも表示される場合があります)。
FATAL ERROR in native method: Using JNIEnv in the wrong thread
at Proxy.monitor_enter_(Native Method)
at Proxy.monitor_enter(Proxy.java:30)
at ex1.LifeThreads.setNeighborThreadChange(LifeThreads.java:36)
at ex1.LifeThreads.neighbor(LifeThreads.java:425)
at ex1.LifeThreads.standardItr(LifeThreads.java:321)
at ex1.LifeThreads.run(LifeThreads.java:462)
(この種の事後分析トレースは、Sun JVM の -Xcheck:jni オプションで取得できます)
コードでは、さまざまな JDK サンプル (ネイティブ メソッドを持つプロキシ Java クラスを介した heapViewer、heapTracker など) に示されているのと同じ種類の計測を行います。各命令Proxy.monitor_enter_
の後にネイティブ メソッドが呼び出されます。monitorenter
これは、monitor_enter_ のコードです。
void native_monitor_exit(JNIEnv *jni, jclass klass, jthread thread_id, jobject obj)
{
scoped_lock( agent::instance()->jvmti(), agent::instance()->monitor_ );
if( agent::instance()->death_active_)
return;
std::string name = agent::instance()->thread_name( thread_id );
thread_t* thread = get_thread( thread_id );
if( thread == 0 )
return;
jobject global_ref = agent::instance()->jni()->NewGlobalRef( obj );
if( global_ref == 0 )
fatal_error("Out of memory while trying to create new global ref.");
logger::instance()->level(1) << "MONITOR ENTER"
<< "\n\t" << "jthread name= " << name
<< "\n\t" << "thread_t= " << thread << " " << *thread
<< "\n\t" << "monitor gl= " << global_ref
<< std::endl;
thread->lock( lock(global_ref) );
}
は、scoped_lock
基本的には JVMTI Raw Monitor の開始/終了用のスコープ ロックで
あり、呼び出されたときにクラスのインスタンス(それ自体は単にグローバル参照をラップするだけ) が格納される、thread_t
一部をラップする単なる構造体です。std::vector
lock
jobject
global_ref
thread->lock( lock(global_ref))
JVMTI 環境。以下に示すように、エージェントシングルトンでグローバルにキャッシュされますが、スレッドローカルであるJNI envは、使用する前に毎回再ロードされます(それほど効率的ではありませんが、今のところ気にしません)。
JNIEnv* jni()
{
jint res;
JNIEnv* env = 0;
res = jvm()->GetEnv( (void**)&env, JNI_VERSION_1_2 );
if( res == JNI_EDETACHED )
{
res = jvm()->AttachCurrentThread( (void **)&env, 0 );
if( res != JNI_OK || env == 0 )
fatal_error( "ERROR: Unable to create JNIEnv by attach, error=%d\n", res );
}
else if( res != JNI_OK || env == 0 )
fatal_error( "ERROR: Unable to create JNIEnv, error=%d\n", res );
}
return env;
}