Skip to content

Java Native Interface

The Java Native Interface (JNI) is a programming framework that enables Java code running in a Java Virtual Machine (JVM) to call, and to be called by, native applications (programs specific to a hardware and operating system platform) and libraries written in other languages such as C, C++ and assembly.

Category

Libraries

Examples

Features

JNI의 기능에 대한 설명.

JNI and Threads

멀티스레드를 사용할 경우 다음과 같은 사항을 주의해야 한다.

JNIEnv pointer는 해당 Thread에서만 Valid하다.
이 JNIEnv pointer를 다른 Thread에 전달하거나, Cache하여 다른 Thread에서 사용해선 안된다. JVM에서 동일한 Thread에서의 연속적인 함수 호출에 대해서는 같은 JNIEnv pointer를 전달하지만, 다른 Thread의 함수 호출할 경우에는 다른 JNIEnv pointer 를 전달합니다. 따라서 multi thread programming 에서는 JNIEnv를 cache 하는 것은 실수이다.
Local reference는 해당 reference를 만든 Thread에서만 Valid하다.
Local reference를 다른 Thread로 전달해서는 안된다. 다른 Thread에서 이 Local reference를 Share하고 싶은 경우에는 반드시 Global reference 로 만들어 Share해야 한다.

참조 포인터 제거

FindClass, NewCharArray, 등으로 획득한 jobject 관련 객체는 사용 후 반드시 DeleteLocalRef 함수로 참조를 제거해야 한다.

JIN_OnLoad

#include <jni.h>

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    if (vm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }
    // env 포인터를 사용하여 Java 메서드 호출 등의 작업을 수행합니다.
    return JNI_VERSION_1_6;
}

The Java™ Native Interface: Programmer’s Guide and Specification

JNI Functions

JNI로 AttachCurrentThread가 -1이 리턴되는 경우

Native Library에서 pthread로 생성된 쓰레드를 자바에서 사용하려면(예를 들어 Callback 함수를 사용한다던가), JNI함수에서 AttachCurrentThread를 이용하여 JNIEnv를 가져와야 하는데, 이걸 가져오지 못하고, -1이 리턴되는 경우가 있습니다. 원인을 살펴보던 중 pthread로 생성될때 기본 스택 크기가 256k였고, 이 크기가 문제가 되는 것 같습니다. pthread_attr_setstacksize()를 이용해서 스택크기를 500k이상으로 주니, 함수가 정상적으로 동작하네요.

생성된 Thread 내에서의 JNIEnv::FindClass가 동작하지 않는다

Android JNI 에서, pthread_create 에 의해 생성된 스레드 내에서 JNIEnv::FindClass 메소드가 항상 실패하고 만다. 구글링해본 결과 정확한 해결책은 찾지 못했고 얻은 힌트에 의해면 해당하는 Class 의 ClassLoader 를 설정해줘야 한다고 한다. (SEE System.setClassLoader) JNIEnv 개체는 Thread Local 값이기 때문에 전역으로 공유해서는 안된다. native 스레드에서 JNI 로 JavaClass 를 호출하려면 JavaVM 의 AttachCurrentThread 나 AttachCurrentThreadAsDaemon 메소드를 통해 JAVA 의 MainThread 그룹에 등록해줘야 한다. 그런데 그렇게 해도 위의 클래스 로더문제 때문에 JNIEnv::FindClass 를 이용해주려면 클래스로더를 등록하던가 아예 초기화 과정(JNI_OnLoad 나 별도의 초기화 Routine)에서 해당하는 클래스의 레퍼런스를 아예 미리 캐시하고 있는 것이 낫다. 그런데 FindClass 에서 얻은 jclass reference 는 LocalReference 이므로 다음과 같이 JNIEnv::NewGlobalRef 를 통해 Global Reference 로 캐시해야 한다.

jclass g_myClass; // 전역에서 정의
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
// ... (중략)
g_myClass = reinterpret_cast<jclass>(myEnv->NewGlobalRef(myClass));

Favorite site

References


  1. Java_additional_jni_features.pdf 

  2. JNI_Programmer’s_Guide_and_Specification.pdf 

  3. How_to_use_JNI_Callback.pdf 

  4. JAVA_Native_Method_(JNI).pdf 

  5. JNI_Tutorial_-_String,_Array.pdf 

  6. How_to_use_JNI.pdf 

  7. Eureka7_com_ne_kr.zip (eureka7.com.ne.kr/jdk-1_5_0-Korean-doc/docs/guide/jni/spec/jniTOC.html 참조) 

  8. Principle_of_operation_JNI.pdf 

  9. How_to_use_JNI_Callback.pdf