설계의 개요 - xrath.com 에서 번역됨

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

## 설계의 개요

## 제 2 장

이 장은 JNI 의 주된 설계의 문제에 초점을 맞추고 있습니다. 본항의 설계의 문제의 대부분은 네이티브 메소드와 관련이 있습니다. 호출 API 의 설계에 대해서는,「 제 5 장」에 게재되고 있습니다.

## JNI 인터페이스의 함수와 포인터

native code는, JNI 함수를 호출해 Java VM 기능에 액세스 합니다. JNI 함수는 「인터페이스 포인터」에 의해 사용할 수 있습니다. 인터페이스 포인터는, 포인터를 가리키는 포인터입니다. 이 포인터는 포인터의 배열을 가리켜, 이 각각의 포인터가 인터페이스 함수를 가리킵니다. 어느 인터페이스 함수도 배열내의 사전에 정의된 오프셋(offset)에 있습니다. 그림 2-1 은, 인터페이스 포인터의 편성을 도시한 것입니다.

그림 2-1 인터페이스 포인터

JNI 인터페이스는, C++ 가상 함수 테이블 또는 COM 인터페이스와 같이 구성되어 있습니다. 고정된 편입 함수 엔트리가 아니고 인터페이스 테이블을 사용하는 이점은, JNI 이름 공간이 native code와 분리할 수 있게 되는 것입니다. VM 는 복수 버젼의 JNI 함수 테이블을 용이하게 제공할 수 있습니다. 예를 들어, VM 는 다음과 같이 2 개의 JNI 함수 테이블을 지원할 수도 있습니다.

- 한편의 테이블은, 부정한 인수의 검사를 엄밀하게 실행하므로, 디버그에 적절하고 있다
- 이제(벌써) 한편의 테이블은, JNI 스펙으로 요구되는 최소한의 검사를 실행해, 거기에 따라 효율이 오른다

JNI 인터페이스 포인터는 현재의 thread안만으로 유효합니다. 따라서, 네이티브 메소드가 thread간에 인터페이스 포인터를 건네주지 않게 해 주세요. JNI 를 구현하고 있는 VM 는, JNI 인터페이스 포인터에 의해 지시받은 영역에 thread의 로컬 데이터를 할당해 포함할 수도 있습니다.

네이티브 메소드는 JNI 인터페이스 포인터를 인수로서 받습니다. 따라서, VM 가 같은 Java thread로부터 네이티브 메소드에 복수의 호출을 실시하는 경우는, 네이티브 메소드에 같은 인터페이스 포인터를 건네주는 것이 보증되고 있습니다. 그러나, 네이티브 메소드는, 다른 Java thread로부터에서도 호출할 수가 있으므로, 다른 JNI 인터페이스 포인터를 받는 일도 있습니다.

## 네이티브 메소드의 로드와 링크

네이티브 메소드는,`System.loadLibrary` 메소드를 사용해 로드 됩니다. 다음의 예에서는, 클래스 초기화 메소드가, 네이티브 메소드 `f` 가 정의되고 있는 플랫폼 고유의 네이티브 라이브러리를 로드하고 있습니다.

    package pkg;

    class Cls {

         native double f(int i, String s);

         static {

             System.loadLibrary(“pkg_Cls”);

         }

    }

`System.loadLibrary` 의 인수는, 프로그래머에 의해 임의에 선택된 라이브러리명입니다. 이 시스템은, 표준이어도 플랫폼 고유의 방식에 따라 라이브러리명을 네이티브 라이브러리명으로 변환합니다. 예를 들어, Solaris 시스템은 `pkg_Cls` 의 이름을 `libpkg_Cls.so` 로 변환하는데 대해, Win32 시스템은 같은 `pkg_Cls` 의 이름을 `pkg_Cls.dll` 로 변환합니다.

프로그래머는, 같은 로더로 클래스가 로드 되는 한, 필요로 하는 클래스가 아무리 있어도, 그 필요한 모든 네이티브 메소드를, 단일 라이브러리를 사용해 포함할 수가 있습니다. VM 는 클래스 로더마다의 로드 된 네이티브 라이브러리의 리스트를 내부적으로 유지합니다. 벤더는, 이름이 가능한 한 충돌하지 않는 네이티브 라이브러리명을 선택할 필요가 있습니다.

기반의 operating system가 동적 링크를 지원하지 않는 경우는, 모든 네이티브 메소드가 VM 와 사전에 링크가 끝난 상태가 아니면 안됩니다. 이 경우, VM 는 실제로는 이 라이브러리를 로드하는 일 없이 `System.loadLibrary` 의 호출을 완료합니다.

프로그래머는 JNI 함수 `RegisterNatives()` 를 호출해, 클래스와 관련지을 수 있었던 네이티브 메소드를 등록할 수도 있습니다. `RegisterNatives()` 함수는, 정적 링크된 함수를 사용하는 경우에 특히 유용합니다.

### 네이티브 메소드명의 해결

동적 링커는 네이티브 메소드의 이름에 근거해 엔트리를 해결합니다. 네이티브 메소드명은, 다음의 컴퍼넌트를 연결해 만들어집니다.

- 접두사 `Java_`
- 분해된 완전 수식 클래스명
- 밑줄 ( 「_」) 단락 문자
- 분해된 메소드명
- overload 된 네이티브 메소드에서는, 2 개의 밑줄 ( 「__」)에 이어 분해된 인수의 시그니챠

VM 는, 네이티브 라이브러리에 상주하는 메소드에 대해 메소드명의 일치를 조사합니다. VM 는, 최초로 쇼트명 (인수의 시그니챠가 없는 이름)을 찾습니다. 다음에 롱명 (인수의 시그니챠가 붙은 이름)을 찾습니다. 프로그래머가 롱명을 사용할 필요가 있는 것은, 네이티브 메소드가 다른 네이티브 메소드에 의해 overload 되었을 때 뿐입니다. 그러나, 네이티브 메소드가 비네이티브 메소드와 같은 이름을 가지고 있는 경우, 이것은 문제가 아닙니다. 비네이티브 메소드 (Java 메소드)는, 네이티브 라이브러리에 상주하고 있습니다.

다음의 예에서는, 네이티브 메소드 `g` 는 롱명을 사용해 링크할 필요는 없습니다. 이제(벌써) 한편의 메소드 `g` 가 네이티브 메소드가 아니기 때문에, 네이티브 라이브러리에 없기 때문입니다.

    class Cls1 {

      int g(int i);

      native int g(double d);

    }

단순한 이름 분해 스킴입니다만, 모든 Unicode 문자가 유효한 C 함수명에 확실히 변환되게 되어 있습니다. 완전 수식 클래스명 중(안)에서 사선 ( 「/」) 대신에 밑줄 ( 「_」) 문자를 사용합니다. 이름 또는 형태 기술자가 숫자로 시작될 것은 없기 때문에,겉(표) 2-1 의 예의 같게,`_0`,...,`_9` (을)를 escape sequence에 사용할 수가 있습니다.

겉(표) 2-1 Unicode 문자 변환
escape sequence | 표시
--- | ---

`_0XXXX`

Unicode 문자 `XXXX`
소문자는 ASCII Unicode 문자
이외의 문자를 나타내는 경우에 사용
되는 것에 주의해 주세요.
예를 들어 `_0abcd` 는
`_0ABCD` 와 구별됩니다.

`_1` | 문자 「_」
`_2` | 시그니챠 안의 문자 「;」
`_3` | 시그니챠 안의 문자 「[」

네이티브 메소드와 인터페이스 API 의 양쪽 모두, 주어진(given) 플랫폼상에서의 표준 라이브러리 호출 규칙에 따르고 있습니다. 예를 들어, UNIX 시스템은 C 호출 규칙을 사용하는데 대해, Win32 시스템은 __stdcall 을 사용합니다.

### 네이티브 메소드의 인수

JNI 인터페이스 포인터는, 네이티브 메소드의 최초의 인수입니다. JNI 인터페이스 포인터는 「JNIEnv」형입니다. 2 번째의 인수는, 네이티브 메소드가 static 일까 static 가 아닌 것처럼 따라 다릅니다. static 가 아닌 네이티브 메소드의 2 번째의 인수는, 객체의 참조입니다. static 인 네이티브 메소드의 2 번째의 인수는, Java 클래스의 참조입니다.

나머지의 인수는, 일반적으로의 Java 메소드 인수에 대응하고 있습니다. 네이티브 메소드 호출은, 호출측 루틴에 결과를 값으로 건네주어 되돌립니다. 제 3 장으로,Java 형과 C 형과의 매핑에 대해 설명하고 있습니다.

코드예 2-1 에, C 함수를 사용해 네이티브 메소드 `f` 를 구현하는 예를 나타냅니다. 네이티브 메소드 `f` 는, 다음과 같이 선언됩니다.

    package pkg;

    class Cls {

         native double f(int i, String s);

         ...

    }

긴 분해명 `Java_pkg_Cls_f_ILjava_lang_String_2` 를 가지는 C 함수는, 네이티브 메소드 `f` 를 구현합니다.

코드예 2-1 C 를 사용하는 네이티브 메소드의 구현

    jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (

         JNIEnv *env,        /* interface pointer */

         jobject obj,        /* "this" pointer */

         jint i,             /* argument #1 */

         jstring s)          /* argument #2 */

    {

         /* Obtain a C-copy of the Java string */

         const char *str = (*env) ->GetStringUTFChars(env, s, 0);

         /* process the string */

         ...

         /* Now we are done with str */

         (*env) ->ReleaseStringUTFChars(env, s, str);

         return ...

    }

항상 인터페이스 포인터 env 를 사용해 Java 객체를 조작하고 있는 것에 주의해 주세요. C++ 를 사용하면(자),코드예 2-2 에 나타내도록(듯이), 다소 상쾌한 코드를 쓸 수가 있습니다.

코드예 2-2 C++ 를 사용하는 네이티브 메소드의 구현

    extern "C" /* specify the C calling convention */

    jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (

         JNIEnv *env,        /* interface pointer */

         jobject obj,        /* "this" pointer */

         jint i,             /* argument #1 */

         jstring s)          /* argument #2 */

    {

         const char *str = env->GetStringUTFChars(s, 0);

         ...

         env->ReleaseStringUTFChars(s, str);

         return ...

    }

C++ 에서는, 여분의 간접 참조 및 인터페이스 포인터 인수가 원시 코드로부터 사라지고 있습니다. 그러나, 기반이 되는 기구는 C 에 의한 경우와 완전히 같습니다. C++ 에서는, JNI 함수가, 인 라인 멤버 함수로서 정의됩니다만, 이것들은 전개되어, C 의 대응 부분이 됩니다.

## Java 객체의 참조

정수, 문자등의 원시형은, Java 와 네이티브 메소드간에 카피됩니다. 한편, 임의의 Java 객체는 참조 인도입니다. VM 는 native code에게 건네진 모든 객체가 가비지 컬렉터에 의해 해제되지 않게, 이러한 객체를 추적하지 않으면 안됩니다. native code는, 반대로, 객체가 이제(벌써) 필요없는 것을 VM 에 통지하는 수단을 가지지 않으면 안됩니다. 게다가 가비지 컬렉터는, native code에 의해 참조되는 객체를 이동하는 일도 할 수 없으면 안됩니다.

### 글로벌 참조 및 로컬 참조

JNI 는, native code에 의해 사용되는 객체 참조를 「로컬 참조」와「글로벌 참조」의 2 개의 카테고리로 나눕니다. 로컬 참조는, 네이티브 메소드 호출동안만 유효해, 네이티브 메소드가 복귀하면(자) 자동적으로 해제됩니다. 글로벌 참조는, 명시적으로 해제될 때까지 유효하게 되어 있습니다.

객체는, 로컬 참조로서 네이티브 메소드에게 건네집니다. JNI 함수에 의해 반환되는 Java 객체는 모두 로컬 참조입니다. JNI 에서는, 프로그래머가 로컬 참조로부터 글로벌 참조를 작성할 수가 있습니다. Java 객체를 취급하는 JNI 함수는, 글로벌과 로컬의 양쪽 모두의 참조를 받아들입니다. 네이티브 메소드는, 그 결과적으로 글로벌 또는로컬의 어느 쪽인가의 참조를 VM 에 돌려주게 됩니다.

대부분의 경우, 프로그래머는, 네이티브 메소드가 돌아온 뒤, VM 에 근거해 모든 로컬 참조를 해제해야 합니다. 그러나, 프로그래머가 명시적으로 로컬 참조를 해제할 필요가 있는 경우도 있습니다. 예를 들어, 다음과 같은 상황이 있습니다.

- 네이티브 메소드가 큰 Java 객체에 액세스 해, 이 Java 객체에 대해서 로컬 참조를 작성한다. 다음에 네이티브 메소드는, 호출 측에 돌려주기 전에 추가의 계산을 실행한다. 큰 Java 객체의 로컬 참조는, 이 객체가 나머지의 계산에 사용되지 않게 되었을 경우에서도, 가베지 컬렉트를 방해할 수 있다
- 네이티브 메소드는 다수의 로컬 참조를 작성하지만, 이러한 모든 것이 동시에 사용되는 것은 아니다. VM 는 로컬 참조를 추적하기 위해(때문에) 일정량의 스페이스를 필요로 해, 그 때문에 많은 로컬 참조를 작성하면(자), 시스템의 메모리가 없어지는 일이 있다. 예를 들어, 네이티브 메소드는, 큰 배열의 객체를 통해 루프 해, 그 요소를 로컬 참조로서 검색해, 반복마다 1 개의 요소로 연산한다. 각 반복의 종료후, 프로그래머는 그 배열 요소의 로컬 참조를 이제 필요로 하지 않는다

JNI 에서는, 프로그래머가 네이티브 메소드내의 임의의 점으로써 로컬 참조를 수동으로 삭제할 수가 있습니다. 프로그래머가 수동으로 로컬 참조를 해제할 수 있는 것을 보증하기 위해(때문에), JNI 함수에서는, 이것들 함수가 결과적으로 돌려주는 참조를 제외해, 여분의 로컬 참조를 작성할 수 없게 되어 있습니다.

로컬 참조는, 이것들이 작성된 thread안만으로 유효합니다. native code는, thread간에 로컬 참조를 수수 해서는 안됩니다.

### 로컬 참조의 구현

로컬 참조자를 구현하기 위해(때문에), Java VM 는 Java 로부터 네이티브 메소드에 제어가 이행 할 때마다 레지스트리를 작성합니다. 레지스트리는, 이동할 수 없는 로컬 참조를 Java 객체에 매핑 해, 객체가 가베지 컬렉트되지 않게 지킵니다. 네이티브 메소드에게 건네지는 모든 Java 객체 (JNI 함수 호출의 결과로서 반환되는 것도 포함한다)는, 자동적으로 레지스트리에 추가됩니다. 이 레지스트리는, 네이티브 메소드가 돌아간 뒤에 삭제되어 그 모든 항목을 가베지 컬렉트할 수 있도록(듯이) 합니다.

레지스트리를 구현하려면 , 테이블, 연결 리스트, 또는 해시 테이블을 사용하는 등, 다양한 방법이 있습니다. 레지스트리안의 항목의 중복을 피하기 (위해)때문에 참조의 카운트가 사용되는 일이 있습니다만, JNI 의 구현에서는 중복 항목을 검출해 중복을 없앨 필요는 없습니다.

로컬 참조는, 엄밀하게 네이티브 스택을 스캔 해도, 충실히 구현할 수 없습니다. native code는, 로컬 참조를 글로벌 또는 heap 데이터 구조에 포함하는 일도 있습니다.

## Java 객체에의 액세스

JNI 는, 글로벌 참조 및 로컬 참조에의 풍부한 액세스 기능세트를 제공합니다. 이것은, VM 가 내부적으로 어떻게 Java 객체를 표현하고 있어도, 같은 네이티브 메소드 구현이 작동하는 것을 의미합니다. 이것이 결정적인 이유가 되어, JNI 는 다양한 VM 구현으로 지원되고 있습니다.

불투명한 참조를 개입시켜 액세스용 함수를 사용하는 오버헤드는, C 데이터 구조체에 직접 액세스 하는 경우보다 높아집니다. 대부분의 경우에 Java 프로그래머는 네이티브 메소드를 사용해, 이 인터페이스의 오버헤드가 눈에 띄지 않게 되는 것 같은 중요한 (자명적이지 않다) 태스크를 실행하고 있다고 생각됩니다.

### 프리미티브(primitive) 배열에의 액세스

이 오버헤드는, 정수열이나 캐릭터 라인과 같이 많은 프리미티브(primitive) 데이터형을 포함하고 있는 큰 Java 객체에서는 받아들여지지 않습니다. 벡터 및 행렬의 계산을 실행하기 위해서 사용되는 네이티브 메소드를 생각해 주세요. Java 배열을 반복 연산해, 각 요소를 모두 함수 호출에 의해 꺼내는 것은, 비효율적입니다.

네이티브 메소드가 VM 에 배열의 내용의 확인을 요구할 수 있도록(듯이), 「피닝」의 개념을 도입하는 해결책도 있습니다. 그 후 네이티브 메소드는, 그 요소를 가리키는 다이렉트 포인터를 받습니다. 그러나, 이 어프로치는 다음의 2 개를 의미합니다.

- 카베이지코레크타는 피닝을 지원해야 한다
- VM 는 프리미티브(primitive) 배열을 메모리에 사이 없게 연속해 배치해야 한다. 이것은 대부분의 프리미티브(primitive) 배열에 있어 가장 자연스러운 구현이지만, 불 배열은 팩에서도 unpack에서도 구현할 수 있다. 따라서, 불 배열의 정확한 배치에 근거하는 native code는 이식할 수 없다

상기의 양쪽 모두의 문제를 극복하는 절충안을 채용하고 있습니다.

제일에, Java 배열의 세그먼트(segment)와 네이티브메모리밧파의 사이에 프리미티브(primitive) 배열 요소를 카피하기 위한 함수세트를 제공합니다. 네이티브 메소드가 큰 배열안의 소수 요소인 만큼 액세스 할 필요 밖에 없는 경우는, 이러한 함수를 사용해 주세요.

제니에, 프로그래머는 다른 함수세트를 사용해, 피닝 된 버젼의 배열 요소를 검색할 수가 있습니다. 이러한 함수가 기억역의 할당 및 카피를 실행하려면 Java VM 가 필요한 일을 기억해 둬 주세요. 이러한 함수가 실제로 배열을 카피할 수 있을지 어떨지는, 다음과 같이 VM 의 구현에 의해 정해집니다.

- 가비지 컬렉터가 피닝을 지원하는 경우, 배열의 배치는 네이티브 메소드가 예기 하는 것 것과 같아서, 카피는 필요없다
- 그 이외의 경우는, 배열을 이동할 수 없는 메모리브록크 (C heap안등)에 카피되어 필요한 포맷 변환이 실행된다. 카피에의 포인터가 반환된다

이 인터페이스는, native code가 배열 요소에 액세스 할 필요가 없어진 것을 VM 에 통지하기 위한 함수를 갖추고 있습니다. 이러한 함수를 호출하면(자), 시스템은 배열의 핀을 제외하는지, 또는 원의 배열을 이동할 수 없는 카피와 적합시켜, 그 카피를 해제합니다.

이것에 의해, 유연성이 높아집니다. 가비지 컬렉터 알고리즘에 의해, 지정 배열마다의 카피 또는 피닝에 대해 개별적으로 판단할 수가 있습니다. 예를 들어, 가비지 컬렉터가 작은 객체를 카피해, 큰 객체를 피닝 할 수도 있습니다.

JNI 의 구현에서는, 복수의 thread로 실행되고 있는 네이티브 메소드가, 동시에 같은 배열에 확실히 액세스 할 수 있도록(듯이) 하지 않으면 안됩니다. 예를 들어, JNI 는 피닝 된 배열 마다 내부 카운터를 갖추어, 어느 thread가, 다른 thread도 피닝 하고 있는 배열의 핀을 제외하는 것이 없게 하고 있습니다. JNI 는 네이티브 메소드에 의한 배타 액세스를 위해서(때문에) 프리미티브(primitive) 배열을 잠글 필요가 없는 것에 주의해 주세요. 다른 thread로부터 동시에 Java 배열을 갱신하면(자), 불측의 결과를 부릅니다.

### 필드 및 메소드에의 액세스

JNI 에서는, native code로 필드에 액세스 해, Java 객체의 메소드를 호출할 수가 있습니다. JNI 는, 심볼명 및 형태의 시그니챠에 의해 메소드 및 필드를 식별합니다. 2 단계의 프로세스에 의해, 이름 및 시그니챠로부터 필드 또는 메소드를 찾아내는 수고를 나누고 있습니다. 예를 들어, 「cls」클래스에서 메소드 `f` 를 호출하는 경우, native code는 우선 다음과 같이 메소드 ID 를 가져옵니다.

    jmethodID mid =      env->GetMethodID(cls, “f”, “(ILjava/lang/String;) D”);

계속되어 native code는, 다음과 같이 메소드 탐색의 수고를 들이지 않고 메소드 ID 를 반복해 사용할 수가 있습니다.

    jdouble result = env->CallDoubleMethod(obj, mid, 10, str);

필드 또는 메소드 ID 에서는, VM 가 그 ID 가 도출된 클래스를 언로드하지 않게 막을 수 없습니다. 클래스가 언로드되면(자), 필드 또는 메소드 ID 는 무효가 됩니다. 그 때문에, native code로 다음의 점을 확인할 필요가 있습니다.

- 기본이 되는 클래스의 라이브 참조를 보관 유지할지 어떨지
- 필드 또는 메소드 ID 를 재계산할지 어떨지

연장된 기간중에 메소드 또는 필드 ID 를 사용할지 어떨지

JNI 는, 필드 또는 메소드 ID 가 어떻게 내부적으로 구현되고 있으려면 어떤 제약도 부과하지 않습니다.

## 프로그래밍 에러의 보고

JNI 는, null 포인터 또는 부정한 인수형의 수수와 같은 프로그래밍 에러에 대해 체크를 실시하지 않습니다. 부정한 인수형에는, Java 클래스 객체 대신에 일반적으로의 Java 객체를 사용 하는것 같은 일이 포함됩니다. JNI 는, 다음과 같은 이유로부터 이러한 프로그래밍 에러에 대한 체크를 실시하지 않습니다.

- JNI 함수에 일어날 수 있는 모든 에러 조건에 대해 체크하도록(듯이) 강제하면(자), 일반적으로의 (정상적인) 네이티브 메소드의 퍼포먼스가 저하한다
- 많은 경우, 이러한 체크를 실행할 수 있을 만큼 충분한 실행시의 정보가 없다

대부분의 C 라이브러리 함수는, program error에 대해서 보호되고 있지 않습니다. 예를 들어,`printf()` 함수는, 무효 주소를 받으면(자), 일반적으로은 실행시 에러를 일으켜, 에러 코드를 돌려주지 않습니다. 모든 일어날 수 있는 에러 조건에 대해 체크하도록(듯이) C 라이브러리 함수에 강제하면(자), 사용자 코드로 1 회 체크해 또 라이브러리에서도 실시한다고 하는 것처럼, 체크가 중복 할 가능성이 있습니다.

프로그래머는 부정한 포인터나 잘못한 형태의 인수를 JNI 함수에 건네주어서는 안됩니다. 이것을 실시하면, 시스템의 파괴 상태 또는 VM 의 크래쉬를 포함한, 불측의 결과에 이르는 일이 있습니다.

## Java 의 예외

JNI 에서는, 네이티브 메소드는 임의의 Java 의 예외를 발생시키는 것이 가능합니다. native code에서도, 미처리의 Java 의 예외를 처리할 수가 있습니다. 미처리대로 되어 있는 Java 의 예외는 VM 에 돌려 보내집니다.

### 예외와 에러 코드

JNI 함수에 따라서는, Java 의 예외 기구를 사용해 에러 조건을 보고하는 것도 있습니다. 대부분의 경우, JNI 함수는, 에러 코드를 돌려주어 Java 의 예외를 throw 하는 것에 의해, 에러 상태를 보고합니다. 일반적으로, 이 에러 코드는, 일반적으로의 반환값의 범위외에 있는 특수한 반환값 (`null` 등)입니다. 따라서, 프로그래머는 다음 일을 실시할 수가 있습니다.

- 마지막 JNI 호출의 반환값을 재빠르게 체크해, 에러가 일어나고 있는지 어떤지 판단한다
- 함수 `ExceptionOccurred()` 를 호출해, 에러 상태의 한층 더 상세한 기술이 포함되어 있는 예외 객체를 취득한다

프로그래머가 최초로 에러 코드를 체크할 수 없는 상태로, 예외를 체크하는 것이 필요하게 되는 경우로서 다음의 2 개의 케이스가 있습니다.

- Java 메소드를 호출하는 JNI 함수가 Java 메소드의 결과를 돌려준다. 프로그래머는 `ExceptionOccurred()` 를 호출해, Java 메소드의 실행중에 일어날 수 있는 예외가 일어나지 않은가 체크해야 한다
- JNI 배열 액세스 함수의 일부에는, 에러 코드를 돌려주지 않지만,`ArrayIndexOutOfBoundsException` 또는 `ArrayStoreException` 를 throw 하는 것이 있다

그 외 모든 경우는, 비에러의 반환값으로, 예외가 throw되어 있지 않은 것을 보증하고 있습니다.

### 비동기인 예외

multi-thread의 경우, 현재의 thread는 아닌 다른 thread가 비동기인 예외를 송신하는 일이 있습니다. 비동기인 예외가, 현thread의 native code의 예외에 곧바로 영향을 줄 것은 없습니다만, 이하의 시점에서 영향을 줍니다.

- native code가, 동기 한 예외를 발생시킬 수가 있는 JNI 함수의 1 개를 호출한다
- native code가 `ExceptionOccurred()` 를 사용해 동기 및 비동기의 예외가 있을까를 명시적으로 체크한다

동기 한 예외를 발생시킬 가능성이 있는 JNI 함수만이 비동기인 예외를 체크하는 것에 주의해 주세요.

네이티브 메소드에서는, 필요한 장소 (다른 예외의 체크가 없는 조밀한 루프안등)에 `ExceptionOccurred()` 의 체크를 삽입해, 현재의 thread가 비동기인 예외에 적당한 시간의 범위내에서 응답하는 것을 보증할 필요가 있습니다.

### 예외의 처리

native code로 예외를 처리하려면 , 다음의 2 방법의 방법이 있습니다.

- 네이티브 메소드는, 네이티브 메소드 호출을 개시하는 Java 코드중에서 throw 되게 되어 있는 예외를 일으켜, 직접 복귀하는 것을 선택할 수 있다
- native code는,`ExceptionClear()` 를 호출해 그 예외를 클리어 하고 나서, 그 예외 처리 코드를 실행할 수 있다

예외의 발생 후, native code는, 다른 JNI 호출을 실시하기 전에 우선 예외를 클리어 할 필요가 있습니다. 미처리의 예외가 있을 때, 안전하게 호출할 수 있는 JNI 함수는 다음과 같습니다.

      ExceptionOccurred()
      ExceptionDescribe()
      ExceptionClear()
      ExceptionCheck()
      ReleaseStringChars()
      ReleaseStringUTFChars()
      ReleaseStringCritical()
      Release<Type>ArrayElements()
      ReleasePrimitiveArrayCritical()
      DeleteLocalRef()
      DeleteGlobalRef()
      DeleteWeakGlobalRef()
      MonitorExit()
      PushLocalFrame()
      PopLocalFrame()

목차 | 전의 항목 | 다음의 항목 |

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