JNI 오브젝트 참조 개요

GC에서 JNI 오브젝트 참조를 찾는 방법에 대한 구현 세부사항은 JNI 스펙에 자세히 설명되어 있지 않습니다. 대신, JNI는 신뢰할 수 있고 예측 가능한 필수 동작을 지정합니다.

로컬 및 글로벌 참조

로컬 참조는 해당 작성 스택 프레임 및 스레드로 범위가 지정되며 해당 작성 스택 프레임에서 리턴하는 경우 자동으로 삭제됩니다. 글로벌 참조를 사용하면 원시 코드가 JVM에 접속된 모든 스레드에서 사용할 수 있는 양식으로 로컬 참조를 승격할 수 있습니다.

글로벌 참조 및 메모리 누수

글로벌 참조는 자동으로 삭제되지 않기 때문에 프로그래머가 메모리 관리를 처리해야 합니다. 모든 글로벌 참조는 해당 참조 대상에 대한 루트를 설정하고 전체 서브트리에 접근할 수 있도록 합니다. 따라서 메모리 누수를 방지하려면 작성된 모든 글로벌 참조가 해제되어야 합니다.

글로벌 참조의 누수는 결국 메모리 부족 예외로 연결됩니다. 특히 JNI 예외 처리를 수행하지 않는 경우에 이러한 예외를 해결하기 어려울 수 있습니다. 예외 처리를 참조하십시오.

JNI 글로벌 참조 기능을 제공하고 참조 대상의 일부 자동 가비지 콜렉션도 제공하려면 JNI에서 다음과 같은 두 개의 함수를 제공해야 합니다.
  • NewWeakGlobalRef
  • DeleteWeakGlobalRef
이러한 함수는 weak 참조에 대한 JNI 액세스를 제공합니다.

로컬 참조 및 메모리 누수

더 이상 범위 내에 있지 않은 로컬 참조의 자동 가비지 콜렉션은 대부분의 상황에서 메모리 누수를 방지합니다. 이 자동 가비지 컬렉션은 네이티브 스레드가 Java로 복귀할 때(네이티브 메서드) 또는 네이티브 스레드 객체( JVM )에서 분리될 때(Invocation API) 발생합니다. 자동 가비지 콜렉션이 발생하지 않는 경우 로컬 참조 메모리 누수가 발생할 수 있습니다. 원시 메소드가 JVM으로 돌아가지 않은 경우 또는 호출 API를 사용하는 프로그램이 JVM에서 분리되지 않은 경우 메모리 누수가 발생할 수 있습니다.

원시 코드가 루프에서 새 로컬 참조를 작성하는 다음 예의 코드를 고려해 보십시오.

while ( <condition> )
{
   jobject myObj = (*env)->NewObject( env, clz, mid, NULL );

   if ( NULL != myObj )
   {
      /* we know myObj is a valid local ref, so use it */
      jclass myClazz = (*env)->GetObjectClass(env, myObj);

      /* uses of myObj and myClazz, etc. but no new local refs */

      /* Without the following calls, we would leak */
      (*env)->DeleteLocalRef( env, myObj ); 
      (*env)->DeleteLocalRef( env, myClazz );
   }

} /* end while */

새 로컬 참조가 루프 내부의 myObjmyClazz 변수를 겹쳐쓰지만 모든 로컬 참조는 루트 세트에 보존됩니다. 이러한 참조는 DeleteLocalRef 호출을 통해 명시적으로 제거되어야 합니다. 해당 DeleteLocalRef 호출이 없으면, 스레드가 Java로 반환되거나 애플리케이션 컨텍스트( JVM )에서 분리될 때까지 로컬 참조가 누출됩니다.

JNI weak 글로벌 참조

weak 글로벌 참조는 특수 유형의 글로벌 참조입니다. 모든 스레드에서 사용될 수 있으며 원시 함수 호출 사이에 사용될 수 있지만 GC 루트로 작동하지 않습니다. GC는 오브젝트에 strong 참조가 없는 경우 언제든지 weak 글로벌 참조에서 참조되는 오브젝트를 처리합니다.

weak 글로벌 참조는 주의하여 사용해야 합니다. weak 글로벌 참조에서 참조되는 오브젝트가 가비지 콜렉션되는 경우 해당 참조는 널 참조가 됩니다. 널 참조는 JNI 함수의 서브세트와 함께 사용되는 경우에만 안전할 수 있습니다. weak 글로벌 참조가 수집되었는지 여부를 테스트하려면 IsSameObject JNI 함수를 사용하여 weak 글로벌 참조를 널값과 비교하십시오.

weak 글로벌 참조는 테스트된 후 또는 JIN 함수 실행 중에도 널 참조가 될 수 있기 때문에 해당 참조가 널이 아닌지 테스트한 경우에도 대부분의 JNI 함수를 weak 글로벌 참조와 함께 호출하는 것은 안전하지 않습니다. 대신, weak 글로벌 참조가 사용되기 전에 항상 strong 참조로 승격해야 합니다. NewLocalRef 또는 NewGlobalRef JNI 함수를 사용하여 weak 글로벌 참조를 승격할 수 있습니다.

weak 글로벌 참조는 메모리를 사용하며 더 이상 필요하지 않은 경우 DeleteWeakGlobalRef JNI 함수를 사용하여 해제되어야 합니다. weak 글로벌 참조를 해제하는 데 실패하면 느린 메모리 누수가 발생하며 결국 메모리 부족 예외로 연결됩니다.

JNI weak 글로벌 참조 사용에 대한 정보 및 경고는 JNI 스펙을 참조하십시오.

JNI 참조 관리

JNI 참조 관리에 대한 플랫폼 독립적 규칙 세트가 있습니다.

이러한 규칙은 다음과 같습니다.

  1. JNI 참조는 JVM에 접속된 스레드에서만 유효합니다.
  2. 다음과 같이 원시 코드의 유효한 JNI 로컬 참조를 가져와야 합니다.
    1. 원시 코드에 대한 매개변수로
    2. JNI 함수 호출의 리턴값으로
  3. NewGlobalRef 또는 NewWeakGlobalRef를 호출하여 다른 유효한 JNI 참조(글로벌 또는 로컬)에서 유효한 JNI 글로벌 참조를 가져와야 합니다.
  4. 널값 참조는 항상 유효하며 JNI 참조(글로벌 또는 로컬) 대신 사용될 수 있습니다.
  5. JNI 로컬 참조는 해당 참조를 작성하는 스레드에서만 유효하며 해당 작성 프레임이 스택에 남아 있는 동안에만 유효한 상태로 유지됩니다.
참고:
  1. 원시 스토리지의 로컬 또는 글로벌 참조를 널값으로 겹쳐 쓰면 해당 참조가 루트 세트에서 제거되지 않습니다. 해당 Delete*Ref JNI 함수를 사용하여 루트 세트에서 참조를 제거하십시오.
  2. 보류 중인 예외가 있는 경우 여러 JNI 함수(예: FindClassNewObject)에서 널값을 리턴합니다. 리턴된 값을 이러한 호출의 널값과 비교하는 것은 JNI ExceptionCheck 함수를 호출하는 것과 의미상 동일합니다. 세부사항은 JNI 스펙을 참조하십시오.
  3. JNI 로컬 참조의 작성 프레임이 리턴된 후에는 상황에 관계없이 해당 참조가 사용되지 않아야 합니다. JNI 로컬 참조를 프로세스의 정적 스토리지에 저장하는 것은 위험합니다.