2008-10-18 2 views
5

JNI 메소드를 사용하여 Java 객체를 빌드 할 때 Java 메소드에 매개 변수로 전달하기 위해 JNI 호출 API를 사용하여 호출합니다. 어떻게 관리합니까? 기억? 내가 free()이 더 복잡 소멸자 방법이있는 C 개체가Invocation API를 사용하는 JNI 메모리 관리

:

는 여기에 내가 함께 일하고거야. 이 C 객체는 Java 객체와 연관되어 있어야하며 Java 객체로 응용 프로그램을 끝내면 C 객체가 더 이상 필요하지 않습니다. 그래서, 내가 instance에 끝났어요 지금, 내가 무엇을

c_object = c_object_create(); 
class = (*env)->FindClass (env, "my.class.name"); 
constructor = (*env)->GetMethodID (env, class, "<init>", "(J)V"); 
instance = (*env)->NewObject (env, class, constructor, (jlong) c_object); 

method = (*env)->GetMethodID (env, other_class, "doSomeWork", "(Lmy.class.name)V"); 
(*env)->CallVoidMethod (env, other_class, method, instance); 

해야합니까

:

내가 너무 (오류 명확성을 위해 생략 검사)처럼 자바 객체를 생성 무엇입니까? 이상적으로는 가비지 수집을 VM에 맡기고 싶습니다. instance으로 끝났을 때 내가 제공 한 포인터에 c_object_destroy()을 호출하면 환상적입니다. 이것이 가능한가?

이와 관련하여 별도의 질문은 이와 같은 방법으로 작성한 Java 엔티티의 범위와 관련이 있습니다. class, constructor 또는 method을 수동으로 출시해야합니까? JNI의 문서는 적절하게 메모리를 관리한다는 주제로 좌절감을 표시합니다.

답변

5

  1. 리소스를 해제하는) (마무리 중에 JNI 메소드를 호출 (등 오브젝트, 파일 기술자) 천연 자원을 회수하기위한 전략의 몇 가지가 있습니다. 어떤 사람들은 recommend against implementing finalize이며, 기본적으로 네이티브 리소스가 해제된다는 것을 확신 할 수 없습니다. 메모리와 같은 리소스의 경우 이것은 아마도 문제가되지 않지만 예를 들어 예측 가능한 시간에 플러시해야하는 파일이 있다면 finalize()는 좋은 생각이 아닙니다.

  2. 수동으로 정리 메소드를 호출하십시오. 이는 리소스를 정리해야한다는 것을 알고있는 특정 시점에 유용 할 때 유용합니다. JNI 코드에서 DLL을 언로드하기 전에 할당 해제해야하는 리소스가있을 때이 메서드를 사용했습니다. DLL을 나중에 다시로드 할 수있게하려면 DLL을 언로드하기 전에 개체가 실제로 할당 해제되었는지 확인해야했습니다. finalize() 만 사용하면이 값이 보장되지 않았을 것입니다. 이것은 finalize() 또는 수동으로 호출 된 정리 메소드에서 자원을 할당 할 수 있도록 (1)과 결합 될 수 있습니다. (어떤 객체가 자신의 클린업 메소드를 호출 할 필요가 있는지를 추적하기 위해 WeakReferences의 정식지도가 필요할 것이다.)

  3. 아마도이 문제를 해결하기 위해 PhantomReference을 사용할 수 있지만 이러한 솔루션이 어떻게 작동하는지 정확하게 알 수는 없습니다.

실제로 JNI 문서에 동의하지 않으면 안됩니다. 로컬 및 글로벌 참조를 관리하는 부분이 좀 더 정교화 될 수 있더라도 대부분의 중요한 문제에 대해서는 매우 명확한 것으로 나타났습니다 (JNI specification).

+0

그것은 분명히, 유감스럽게도 필요한 특정 측면입니다 :) valgrind (리눅스 메모리 검사 도구)에서 JVM을 실행하려고 시도했는지는 모르겠지만 너무 많은 용의자 작업을 읽는 것은 불가능합니다. 출력, 그래서 메모리 할당 작업에 대한 명확한 의사 결정이 중요합니다. –

+0

사실, 나는 가지고 있습니다. Valgrind에서 Eclipse를 실행하려고 시도했지만 실패했습니다. http://stackoverflow.com/questions/189284/running-eclipse-under-valgrind. – JesperE

0

GC는 인스턴스를 수집하지만 네이티브 코드에 할당 된 비 자바 힙 메모리를 자동으로 해제하지는 않습니다. c_object 인스턴스를 해제하려면 클래스에 명시적인 메소드가 있어야합니다.

이것은 finalizer가 c_object가 릴리스되어 있는지 확인하고 사용하지 않을 경우 메시지를 기록하는 것을 권장하는 경우 중 하나입니다.

유용한 기술은 Java 클래스 생성자에서 Throwable 인스턴스를 생성하여 필드에 저장하거나 인라인 필드를 초기화하는 것입니다. 파이널 라이저가 클래스가 적절히 배치되지 않았 음을 감지하면 스택 스택을 찾아 스택 스택을 인쇄합니다.

스트레이트 JNI를 피하고 gluegen 또는 Swig (코드를 생성하고 정적으로 링크 할 수 있음)을 사용하지 않는 것이 좋습니다.

+0

그 응용 프로그램은 주로 정적으로 링크 된 기능으로 구성되어 있습니다. 즉, Java 코드를 호출하는 것과는 달리 공유 라이브러리에서 C 코드를 호출하는 것을 주로 다루는 도구 C - 솔루션에 맞지 않습니다. 그럼에도 불구하고 고마워. –

+0

답변을 업데이트했습니다. 정적으로 링크 된 프로젝트에서 swig를 사용했습니다. Gluegen은 더 잘 보이지만 (주석 없이는 거의 작동한다고 가정) 성숙하지는 않습니다. – ddimitrov

1

Re : "별개이지만 관련 질문"... "로컬"컨텍스트에서 사용할 때 jclass, jfieldID 및 jmethodID를 수동으로 릴리스 할 필요가 없습니다. jclass, jfieldID, jmethodID가 아닌 모든 실제 오브젝트 참조는 DeleteLocalRef와 함께 릴리스되어야합니다.

+0

로컬 스택이 VM 호출 스택에없는 경우에도 마찬가지입니다. 저는이 스택에서 VM을 호출하고 있습니다. 다른 방법은 아닙니다 ... –

+0

JNIEnv * 포인터가 있으면 언제든지 VM에 연결되어 있습니다. VM (또는 클래스 로더)이 사라진 후에 jclass, jfieldID, jmethodID를 유지하려고하지 않는 한 괜찮습니다. –

10

JNI 사양은 JNI 메서드 here에서 생성 된 Java 개체를 "소유"하는 사람의 문제를 다룹니다. 로컬글로벌 참조를 구별해야합니다.

JVM이 JNI를 네이티브 코드로 호출 할 때 호출 중에 작성된 모든 객체를 추적하도록 레지스트리를 설정합니다. 네이티브 호출 중에 작성된 모든 객체 (즉, JNI 인터페이스 함수로부터 반환 된 객체)는이 레지스트리에 추가됩니다. 이러한 객체에 대한 참조는 로컬 참조으로 알려져 있습니다. 원시 메소드가 JVM으로 리턴되면, 원시 메소드 호출 중에 작성된 모든 로컬 참조가 파기됩니다. 네이티브 메소드 호출 중에 JVM으로 다시 콜을하는 경우, 컨트롤이 네이티브 메소드로 되돌아 왔을 때 로컬 참조는 여전히 유효합니다. 네이티브 코드에서 호출 된 JVM이 네이티브 코드로 다시 호출하면 로컬 참조의 새 레지스트리가 만들어지고 동일한 규칙이 적용됩니다.

실제로 JNI 인터페이스를 사용하여 JVM을 생성함으로써 (JNIEnv * 포인터를 수신 함) 명령 행에서 주어진 클래스를 찾으면 (즉, java.exe) 자신을 구현할 수 있습니다() 메소드를 호출합니다.

JNI 인터페이스 메소드에서 리턴 된 모든 참조는 로컬입니다. 즉, 정상적인 환경에서는 JNI 메소드에 의해 리턴 된 참조를 수동으로 할당 해제 할 필요가 없습니다. JVM으로 리턴 할 때 파괴되므로 참조를 수동으로 할당 해제 할 필요가 없습니다. 때로는 JVM으로 돌아 가기 전에 삭제할 로컬 참조가 많이있을 때와 같이 "조기에"파기하려고합니다.

전역 참조는 NewGlobalRef()를 사용하여 로컬 참조에서 만들어집니다. 특수 레지스트리에 추가되므로 수동으로 할당을 해제해야합니다. 전역 참조는 네이티브 코드가 여러 JNI 호출에 대한 참조를 보유해야하는 Java 객체에만 사용됩니다 (예 : 네이티브 코드 트리거 이벤트가 Java로 다시 전파되어야하는 경우). 이 경우 JNI 코드는 이벤트를 수신 할 Java 객체에 대한 참조를 저장해야합니다.

희망 사항은 메모리 관리 문제를 조금 분명히 밝힙니다.