호출 API - xrath.com 에서 번역됨

목차 | 전의 항목 | Java Native Interface 스펙

## 호출 API

## 제 5 장

이 호출 API 에 의해, 소프트웨어 벤더는 Java VM 를 임의의 네이티브 어플리케이션에 로드할 수 있게 됩니다. 그 벤더의 제공하는 Java 가 실행 가능한 어플리케이션은, Java VM 원시 코드에 링크할 필요가 없습니다.

이 장에서는, 호출 API 의 개요의 설명으로부터 시작합니다. 그 이후는, 모든 호출 API 함수의 레퍼런스 페이지입니다.

## 개요

다음의 코드예에서는, 호출 API 의 함수의 사용 방법에 대해 설명합니다. 이 예에서는, C++ 코드는 Java VM 를 생성해,`Main.test` 로 불리는 static 메소드를 기동합니다. 명확하게 하기 위해서, 에러 체크를 생략 했습니다.

        #include <jni.h>       /* where everything is defined */
        ...
        JavaVM *jvm;       /* denotes a Java VM */
        JNIEnv *env;       /* pointer to native method interface */
        JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
        JavaVMOption* options = new JavaVMOption[1];
        options[0]. optionString = "-Djava.class.path=/usr/lib/java";
        vm_args.version = JNI_VERSION_1_6;
        vm_args.nOptions = 1;
        vm_args.options = options;
        vm_args.ignoreUnrecognized = false;
        /* load and initialize a Java VM, return a JNI interface
         * pointer in env */
        JNI_CreateJavaVM(&jvm, &env, &vm_args);
        delete options;
        /* invoke the Main.test method using the JNI */
        jclass cls = env->FindClass("Main");
        jmethodID mid = env->GetStaticMethodID(cls, "test", "(I) V");
        env->CallStaticVoidMethod(cls, mid, 100);
        /* We are done.  */
        jvm->DestroyJavaVM();

이 예에서는, API 의 3 개의 함수를 사용하고 있습니다. 호출 API 는 네이티브 어플리케이션이 JNI 인터페이스 포인터를 사용해 VM 기능에 액세스 할 수 있도록(듯이) 합니다. 이 설계는 Netscape 의 JRI 매입 인터페이스와 같습니다.

### VM 의 생성

`JNI_CreateJavaVM()` 함수는 Java VM 를 로드해 초기화해, JNI 인터페이스 포인터에 포인터를 돌려줍니다. `JNI_CreateJavaVM()` 를 호출한 thread는, 「메인 thread」라고 보입니다.

### VM 에의 접속

JNI 인터페이스 포인터 (`JNIEnv`)는, 현재의 thread에서만 유효합니다. 다른 thread가 Java VM 에 액세스 할 필요가 있는 경우, 이것은 최초로 `AttachCurrentThread()` 를 호출해, 그 자체를 VM 에 접속해 JNI 인터페이스 포인터를 취득할 필요가 있습니다. 한 번 VM 에 접속되면(자), 네이티브 thread는 네이티브 메소드내에서 실행중의 보통 Java thread와 같이 기능합니다. 네이티브 thread는, 그 자체를 분리하기 위해서 `DetachCurrentThread()` 를 호출할 때까지 VM 에 접속된 채로 있습니다.

접속된 thread에는, 상당양의 작업을 실행하는데 충분한 스택 영역이 필요합니다. thread마다의 스택 영역의 할당은, operating system 고유합니다. 예를 들어, pthread 를 사용하는 경우는,`pthread_create` 에의 `pthread_attr_t` 인수로 스택 사이즈를 지정할 수가 있습니다.

### VM 로부터의 분리

VM 에 접속된 네이티브 thread는, 종료하기 전에 반드시 `DetachCurrentThread()` 를 호출해 VM 로부터 분리합니다. 호출 스택상에 Java 메소드가 있는 경우, thread는 분리할 수 없습니다.

### VM 의 언로드

`JNI_DestroyJavaVM()` 함수는 Java VM 를 언로드합니다. JDK/JRE 1.1 에서는,`DestroyJavaVM` 를 호출해 VM 를 언로드할 수 있는 것은 메인 thread만이었습니다. JDK/JRE 1.2 에서는, 이 제약이 없어져, 어느 thread에서도 `DestroyJavaVM` 를 호출해 VM 를 언로드할 수 있습니다.

VM 는,현재의thread가demon가 아닌유일한 사용자 thread가 될 때까지 대기해, 그 후 언로드를 실행합니다. 사용자 thread에는, Java thread 및 접속된 네이티브 thread의 양쪽 모두가 있습니다. 이 제한은, Java thread 또는 접속된 네이티브 thread가 락, 윈도우등의 system resource를 보관 유지하고 있을 가능성이 있기 위해서(때문에) 존재합니다. `VM` 는, 자동적으로 이러한 자원을 해제할 수 없습니다. VM 가 언로드되고 있을 때,현재의thread를 실행중의 thread로 한정하는 것으로, 임의의 thread에 의해 보관 유지되는 system resource의 해제을 프로그래머에게 맡깁니다.

## 라이브러리 및 버젼 관리

JDK/JRE 1.1 에서는, 네이티브 라이브러리를 한 번 로드하면(자), 모든 클래스 로더로부터 그 라이브러리를 인식할 수 있었습니다. 그 때문에, 다른 클래스 로더의 2 살의 클래스가, 같은 네이티브 메소드에 링크해 버릴 가능성이 있었습니다. 이 때문에 다음의 2 개의 문제가 발생합니다.

- 어느 클래스는, 다른 클래스 로더내에서 같은 이름을 가지는 클래스가 로드한 네이티브 라이브러리와 잘못해 링크해 버릴 가능성이 있다
- 네이티브 메소드는, 다른 클래스 로더로부터의 클래스와 용이하게 혼동 되어 버리는 일이 있다. 이것에 의해, 클래스 로더에 의해 지정된 이름 공간의 분류가 파괴되어 형태의 안전성의 문제가 일으켜진다

JDK/JRE 1.2 에서는, 클래스 로더가 각자의 네이티브 라이브러리세트를 관리합니다. 같은 JNI 네이티브 라이브러리를, 2 개(살) 이상의 클래스 로더에 로드할 수 없습니다. 로드하려고 하면(자),`UnsatisfiedLinkError` 가 발생합니다. 예를 들어,`System.loadLibrary` 를 사용해 2 개의 클래스 로더에 네이티브 라이브러리를 로드하려고 하면(자),`UnsatisfiedLinkError` 가 발생합니다. 이 새로운 수법의 이점은 다음과 같습니다.

- 클래스 로더에 근거하는 이름 공간의 분류는, 네이티브 라이브러리에 보관된다. 네이티브 라이브러리는, 다른 클래스 로더로부터의 클래스를 용이하게 혼동 할 것은 없다
- 더해, 네이티브 라이브러리는, 대응하는 클래스 로더의 가베지 컬렉션시에 언로드할 수 있다

버젼 관리 및 자원 관리를 용이하게 하기 위해서, JDK/JRE 1.2 의 JNI 라이브러리는 다음의 2 개의 함수를 옵션으로 export 할 수가 있습니다.

### JNI_OnLoad

`jint JNI_OnLoad(JavaVM *vm, void *reserved);`

> 네이티브 라이브러리가 로드 되면(자) (예를 들어 `System.loadLibrary` 에 의해), VM 는 `JNI_OnLoad` 를 호출합니다. JNI_OnLoad 는, 네이티브 라이브러리가 필요로 하는 JNI 버젼을 돌려주지 않으면 안됩니다.

> 새로운 JNI 함수의 어떤 것인지를 사용하기 위해서, 네이티브 라이브러리는 `JNI_VERSION_1_2` 를 돌려주는 `JNI_OnLoad` 함수를 export 할 필요가 있습니다. 네이티브 라이브러리가 `JNI_OnLoad` 함수를 export 하지 않는 경우, VM 는 라이브러리가 JNI 버젼 `JNI_VERSION_1_1` 를 요구하고 있는 것만으로 있으면(자) 간주합니다. VM 가 `JNI_OnLoad` 에 의해 반환된 버젼 번호를 인식하지 않는 경우, 네이티브 라이브러리를 로드할 수 없습니다.

##### 링키지:

네이티브 메소드 구현을 포함한 네이티브 라이브러리로부터 export 됩니다.

##### 도입된 버젼:

JDK/JRE 1.4

> JDK/JRE 1.1 으로 이용할 수 있던 JNI 함수에 가세해 J2SE 릴리스 1.2 에 도입된 JNI 함수를 사용하는 경우, 네이티브 라이브러리는 `JNI_VERSION_1_2` 를 돌려주는 `JNI_OnLoad` 함수를 export 할 필요가 있습니다.

> 릴리스 1.2 로 이용할 수 있던 JNI 함수에 가세해 J2SE 릴리스 1.4 에 도입된 JNI 함수를 사용하는 경우, 네이티브 라이브러리는 `JNI_VERSION_1_4` 를 돌려주는 `JNI_OnLoad` 함수를 export 할 필요가 있습니다.

> 네이티브 라이브러리가 `JNI_OnLoad` 함수를 export 하지 않는 경우, VM 는 라이브러리가 JNI 버젼 `JNI_VERSION_1_1` 를 요구하고 있는 것만으로 있으면(자) 간주합니다. VM 가 `JNI_OnLoad` 에 의해 반환된 버젼 번호를 인식하지 않는 경우, 네이티브 라이브러리를 로드할 수 없습니다.

### JNI_OnUnload

`void JNI_OnUnload(JavaVM *vm, void *reserved);`

> 네이티브 라이브러리를 포함한 클래스 로더의 가베지 컬렉션 시에, VM 는 `JNI_OnUnload` 를 호출합니다. 이 함수는, 클린 업 오퍼레이션에 사용됩니다. 이것은 미확인의 문맥 (파이나라이자로부터의 문맥등)으로 불려 가는 함수이므로, 프로그래머는 신중하게 Java VM 서비스를 사용할 필요가 있습니다. 또 Java 콜백을 임의에 실시할리가 없게 하지 않으면 안됩니다.

`JNI_OnLoad` 와 `JNI_OnUnload` 는, JNI 라이브러리가 옵션으로 제공하는 2 개의 함수이며, VM 로부터 export 되는 것이 아닙니다.

##### 링키지:

네이티브 메소드 구현을 포함한 네이티브 라이브러리로부터 export 됩니다.

##

## 호출 API 의 함수

`JavaVM` 형은 호출해 API 함수 테이블의 포인터입니다. 다음의 코드예에서는, 이 함수 테이블을 나타냅니다.

    typedef const struct JNIInvokeInterface *JavaVM;

    const struct JNIInvokeInterface ... = {
        NULL,
        NULL,
        NULL,

        DestroyJavaVM,
        AttachCurrentThread,
        DetachCurrentThread,

        GetEnv,

        AttachCurrentThreadAsDaemon
    };

다음의 3 개의 호출 API 함수에 주의해 주세요. `JNI_GetDefaultJavaVMInitArgs()`,`JNI_GetCreatedJavaVMs()`, 및 `JNI_CreateJavaVM()` 는, Java VM 함수가 아닙니다. 이러한 함수는 기존의 `JavaVM` 구조체가 없어도 사용할 수가 있습니다.

### JNI_GetDefaultJavaVMInitArgs

`jint JNI_GetDefaultJavaVMInitArgs(void *vm_args);`

Java VM 의 디폴트 구성을 돌려줍니다. 이 함수를 호출하기 전에, native code는, vm_args->version 필드를, VM 를 지원할 수 있는 JNI 버젼으로 설정해 두지 않으면 안됩니다. 이 함수로부터 복귀하면(자), vm_args->version 는, VM 가 지원하는 실제의 JNI 버젼으로 설정됩니다.

##### 링키지:

Java 가상 머신을 구현하는 네이티브 라이브러리로부터 export 됩니다.

##### 파라미터:

`vm_args`:디폴트 인수가 들어가는 `JavaVMInitArgs` 구조체를 참조하는 포인터.

##### 반환값:

요구된 버젼이 지원되고 있는 경우는 `JNI_OK`, 요구된 버젼이 지원되어 있지 않은 경우는 JNI 에러 코드 (부의 수)를 돌려줍니다.

### JNI_GetCreatedJavaVMs

`jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs);`

작성된 Java VM 를 모두 돌려줍니다. VM 에의 포인터는, 작성된 순서에 버퍼 vmBuf 에 기입해집니다. 그러나, 엔트리의 bufLen 번호 밖에 기입하지 않습니다. 작성된 VM 의 전체수는, *nVM 로 돌려줍니다.

JDK/JRE 1.2 에서는, 1 개의 프로세스에서의 복수의 VM 의 작성은 지원되고 있지 않습니다.

##### 링키지:

Java 가상 머신을 구현하는 네이티브 라이브러리로부터 export 됩니다.

##### 파라미터:

`vmBuf`:VM 구조체가 배치되는 버퍼에의 포인터

`bufLen`:버퍼의 길이

`nVMs`:정수를 참조하는 포인터

##### 반환값:

성공하면 `JNI_OK` 를 돌려주어, 실패하면 해당하는 JNI 에러 코드 (부의 수)를 돌려줍니다.

### JNI_CreateJavaVM

`jint JNI_CreateJavaVM(JavaVM **p_vm, JNIEnv **p_env, void *vm_args);`

로드해, Java VM 를 초기화합니다. 현재의 thread가 메인 thread가 됩니다. `env` 인수를, 메인 thread의 JNI 인터페이스 포인터에 설정합니다.

JDK/JRE 1.2 에서는, 1 개의 프로세스에서의 복수의 VM 의 작성은 지원되고 있지 않습니다.

`JNI_CreateJavaVM` 의 2 번째의 인수는 반드시 `JNIEnv *` 를 참조하는 포인터입니다만, 3 번째의 인수는, 옵션 캐릭터 라인을 사용해 임의의 VM기동 옵션을 encode 하는 `JavaVMInitArgs` 구조체를 참조하는 포인터가 됩니다.

    typedef struct JavaVMInitArgs {
        jint version;

        jint nOptions;
        JavaVMOption *options;
        jboolean ignoreUnrecognized;
    } JavaVMInitArgs;

`version` 필드는 `JNI_VERSION_1_2` 이후로 설정할 필요가 있습니다. `options` 필드는, 다음의 형태의 배열입니다.

    typedef struct JavaVMOption {
        char *optionString;  /* the option as a string in the default platform encoding */
        void *extraInfo;
    } JavaVMOption;

배열의 사이즈는,`JavaVMInitArgs` 의 nOptions 필드에 나타납니다. ``ignoreUnrecognized` 가 `JNI_TRUE` 의 경우,`JNI_CreateJavaVM` 는, 「`-X`」또는 「`_`」으로 시작되는 모든 인식할 수 없는 옵션 캐릭터 라인을 무시합니다. ignoreUnrecognized` 가 `JNI_FALSE` 로 설정되어 있는 경우,`JNI_CreateJavaVM` 는 인식할 수 없는 옵션 캐릭터 라인에 조우하면(자), 즉시 `JNI_ERR` 를 돌려줍니다. 모든 Java 가상 머신은, 다음의 표준 옵션세트를 인식할 필요가 있습니다.

> optionString | 설명
> --- | ---
> `-D<name>=<value>` | 시스템 프로퍼티을 설정한다
> `-verbose[:class|gc|jni]` | 장황 출력을 유효하게 한다. 각 옵션의 뒤에, VM 가 인쇄하는 메세지의 종류를 나타내는, 콤마로 단락지은 이름의 리스트를 계속할 수가 있다. 예를 들어, 「`-verbose:gc, class`」는, VM 에 GC 와 클래스 로딩 관련의 메세지를 인쇄하도록(듯이) 지정한다. 표준적인 이름에는,`gc`,`class`, 및 `jni` 등이 있다. 표준이 아닌 (VM 고유의) 이름은 모두, 「`X`」로 시작될 필요가 있다.
> `vfprintf` | `extraInfo` 는,`vfprintf` 훅에의 포인터
> `exit` | `extraInfo` 는,`exit` 훅에의 포인터
> `abort` | `extraInfo` 는,`abort` 훅에의 포인터

더해, 각 VM 구현은, 표준이 아닌 독자적인 옵션 캐릭터 라인세트를 지원합니다. 표준이 아닌 옵션명은, 「`-X`」또는 밑줄 ( 「`_`」)로 시작될 필요가 있습니다. 예를 들어, JDK/JRE 에서는 `-Xms` 및 `-Xmx` 옵션을 지원하고 있기 (위해)때문에, 프로그래머는 초기 및 최대의 heap 사이즈를 지정할 수 있습니다. `-X`」로 시작되는 옵션은, 「`java`」커멘드행으로부터 액세스 가능합니다.

다음의 예는, JDK/JRE 로 Java 가상 머신을 작성하는 코드입니다.

    JavaVMInitArgs vm_args;
    JavaVMOption options[4];

    options[0]. optionString = "-Djava.compiler=NONE";           /* disable JIT */
    options[1]. optionString = "-Djava.class.path=c:\myclasses"; /* user classes */
    options[2]. optionString = "-Djava.library.path=c:\mylibs";  /* set native library path */
    options[3]. optionString = "-verbose:jni";                   /* print JNI-related messages */

    vm_args.version = JNI_VERSION_1_2;
    vm_args.options = options;
    vm_args.nOptions = 4;
    vm_args.ignoreUnrecognized = TRUE;

    /* Note that in the JDK/JRE, there is no longer any need to call
     * JNI_GetDefaultJavaVMInitArgs.
     */
    res = JNI_CreateJavaVM(&vm, (void **) &env, &vm_args);
    if (res < 0) ...

##### 링키지:

Java 가상 머신을 구현하는 네이티브 라이브러리로부터 export 됩니다.

##### 파라미터:

`p_vm`:결과의 VM 구조체가 배치되는 위치에의 포인터

`p_env`:메인 thread의 JNI 인터페이스 포인터가 배치되는 위치에의 포인터

`vm_args`:Java VM 초기화 인수

##### 반환값:

성공하면 `JNI_OK` 를 돌려주어, 실패하면 해당하는 JNI 에러 코드 (부의 수)를 돌려줍니다.

###

### DestroyJavaVM

`jint DestroyJavaVM(JavaVM *vm);`

Java VM 를 언로드해, 그 자원을 회복합니다.

JDK/JRE 1.1 에서는,`DestroyJavaVM` 는 완전하게 지원되고 있는 것은 아닙니다. JDK/JRE 1.1 에서는, 메인 thread만이 `DestroyJavaVM` 를 호출할 수가 있습니다. JDK/JRE 1.2 이후, 어느 thread에서도, 접속되고 있는지 어떤지에 관계없이, 이 함수를 호출할 수 있습니다. 현재의 thread가 접속되고 있는 경우, VM 는, 현재의 thread가demon가 아닌유일한 사용자 레벨 Java thread가 될 때까지 대기합니다. 현재의 thread가 접속되어 있지 않은 경우는, VM 가 현재의 thread를 접속해, 현재의 thread가demon가 아닌유일한 사용자 레벨의 thread가 될 때까지 대기합니다. 다만, VM 의 언로드는, JDK/JRE 에서도 지원되지 않습니다.

##### 링키지:

JavaVM 인터페이스 함수 테이블의 인덱스 3

##### 파라미터:

`vm`:파괴되는 Java VM

##### 반환값:

성공하면 `JNI_OK` 를 돌려주어, 실패하면 해당하는 JNI 에러 코드 (부의 수)를 돌려줍니다.

JDK/JRE 1.1. 2 에서는, VM 의 언로드는 지원되고 있지 않습니다.

###

### AttachCurrentThread

``

`jint AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args);`

현재의 thread를 Java VM 에 접속합니다. `JNIEnv` 인수로 JNI 인터페이스 포인터를 돌려줍니다.

벌써 접속되고 있는 thread에의 접속은, 무조작입니다.

네이티브 thread를 2 개의 Java VM 에 동시에 접속할 수 없습니다.

thread가 VM 에 접속되고 있는 경우, 문맥 클래스의 로더는, bootstrap loader입니다.

##### 링키지:

JavaVM 인터페이스 함수 테이블의 인덱스 4

##### 파라미터:

`vm`:현재의 thread가 접속되는 VM

`p_env`:현재의 thread의 JNI 인터페이스 포인터가 배치되는 위치에의 포인터

`thr_args`:NULL 또는 `JavaVMAttachArgs` 구조체를 참조하는 포인터로 해, 추가 정보를 지정합니다.

JDK/JRE 1.1 에서는,`AttachCurrentThread` 의 2 번째의 인수는, 항상 `JNIEnv` 를 참조하는 포인터입니다. `AttachCurrentThread` 에의 3 번째의 인수는 예약되고 있어`null` 로 설정하지 않으면 안됩니다.

JDK/JRE 1.2 로 1.1 의 동작을 시키는 경우에는, 3 번째의 인수로서 `null` 를 건네줍니다. 또는 다음의 구조체에 포인터를 건네주어, 추가 정보를 지정할 수가 있습니다.

>     typedef struct JavaVMAttachArgs {
>         jint version;  /* must be at least JNI_VERSION_1_2 */
>         char *name;    /* the name of the thread as a modified UTF-8 string, or NULL */
>         jobject group; /* global ref of a ThreadGroup object, or NULL */
>     } JavaVMAttachArgs

##### 반환값:

성공하면 `JNI_OK` 를 돌려주어, 실패하면 해당하는 JNI 에러 코드 (부의 수)를 돌려줍니다.

### AttachCurrentThreadAsDaemon

`jint AttachCurrentThreadAsDaemon(JavaVM* vm, void** penv, void* args);`

`AttachCurrentThread` 와 시멘틱스는 같습니다만, 새롭게 작성 `java.lang.Thread` 인스턴스는 「demon」입니다.

thread가 벌써 `AttachCurrentThread` 또는 `AttachCurrentThreadAsDaemon` 를 개입시켜 접속되고 있는 경우, 이 루틴은 `penv` 로 포인트 되고 있는 값을 현재의 thread의 `JNIEnv` 로 설정합니다. 이 경우,`AttachCurrentThread` 도 이 루틴도, thread의 「demon」에게 영향을 주지 않습니다.

##### 링키지:

JavaVM 인터페이스 함수 테이블의 인덱스 7.

##### 파라미터:

`vm`:현재의 thread가 접속되는 가상 머신 인스턴스

`penv`:현재의 thread의 `JNIEnv` 인터페이스 포인터가 배치되는 위치에의 포인터

`args`:`JavaVMAttachArgs` 구조체에의 포인터

##### 반환값

성공하면 `JNI_OK` 를 돌려주어, 실패하면 해당하는 JNI 에러 코드 (부의 수)를 돌려줍니다.

##### 예외

없음

##### 도입된 버젼:

JDK/JRE 1.4

###

### DetachCurrentThread

`jint DetachCurrentThread(JavaVM *vm);`

Java VM 로부터 현재의 thread를 분리합니다. 이 thread가 보관 유지하는 Java 모니터는 모두 해제됩니다. 이 thread가 종료하는 것을 기다리는 Java thread 모두에게, 통지를 합니다.

JDK/JRE 1.2 에서는, VM 로부터 메인 thread를 떼어낼 수가 있습니다.

##### 링키지:

JavaVM 인터페이스 함수 테이블의 인덱스 5.

##### 파라미터:

`vm`:현재의 thread가 분리되는 VM

##### 반환값:

성공하면 `JNI_OK` 를 돌려주어, 실패하면 해당하는 JNI 에러 코드 (부의 수)를 돌려줍니다.

### GetEnv

`jint GetEnv(JavaVM *vm, void **env, jint version);`

##### 링키지:

JavaVM 인터페이스 함수 테이블의 인덱스 6.

##### 파라미터:

`vm`:인터페이스 취득원의 가상 머신 인스턴스.
`env`:현재의 thread의 JNI 인터페이스 포인터의 배치 위치를 참조하는 포인터.
`version`:요구된 JNI 버젼.

##### 반환값:

현재의 thread가 VM 에 접속되어 있지 않은 경우,`*env` 를 `null` 로 설정해,`JNI_EDETACHED` 를 돌려줍니다. 지정한 버젼이 지원되어 있지 않은 경우,`*env` 를 `null` 로 설정해,`JNI_EVERSION` 를 돌려줍니다. 그 이외의 경우는,`*env` 를 적절한 인터페이스로 설정해,`JNI_OK` 를 돌려줍니다.

##### 도입된 버젼:

JDK/JRE 1.2

목차 | 전의 항목 |

Copyright © 2005 Sun Microsystems, Inc. All rights reserved.