JNI를 통해 C와 Java 사이의 포인터를 전달합니다
-
06-07-2019 - |
문제
현재 Cuda 기능을 사용하는 Java 신청을 만들려고 노력하고 있습니다. Cuda와 Java의 연결은 잘 작동하지만 또 다른 문제가 있고 그것에 대한 내 생각이 올바른지 물어보고 싶었습니다.
Java에서 기본 기능을 호출하면 일부 데이터를 전달하고 기능은 무언가를 계산하고 결과를 반환합니다. 첫 번째 함수가 JNI로 전달할 수있는이 결과에 대한 참조 (포인터)를 반환하고 결과와 함께 추가 계산을하는 다른 기능을 호출 할 수 있습니까?
저의 아이디어는 GPU 메모리에 데이터를 남겨두고 다른 기능이 사용할 수 있도록 데이터를 GPU로 가져 와서 GPU로 복사하여 오버 헤드를 줄이는 것이 었습니다.
시간을 시도한 후에는 응용 프로그램이 종료 된 후 (이 경우 C- 기능이 종료 될 때) 포인터가 삭제되기 때문에 이것은 나 자신을 위해 생각하지 않아야한다고 생각했습니다. 이 올바른지? 아니면 솔루션을보기 위해 C에서 나쁘게 나가는가?
편집 : 글쎄, 질문을 약간 확장하려면 (또는 더 명확하게 만들려면) : JNI 기본 함수에 의해 메모리가 할당되어 있습니까? 아니면 JNI 애플리케이션이 종료 될 때까지 또는 수동으로 자유롭게 해방 될 때까지 여전히 액세스 할 수 있습니까?
입력 해 주셔서 감사합니다 :)
해결책
다음 접근 방식을 사용했습니다.
JNI 코드에서 필요한 객체에 대한 참조를 보유하는 구조물을 만듭니다. 이 구조물을 처음 만들 때, 포인터를 Java로 반환합니다. long
. 그런 다음 Java에서 당신은 이것으로 모든 방법을 호출합니다. long
매개 변수로서 C에서 구조물에 대한 포인터로 시전합니다.
구조는 힙에 있으므로 다른 JNI 호출 사이에 지워지지 않습니다.
편집 : 긴 ptr =를 사용할 수 있다고 생각하지 않습니다. (long)&address;
주소는 정적 변수이므로. Gunslinger47이 제안한 방식으로 사용하십시오. 즉, 새로운 클래스 또는 구조물 (새 또는 malloc 사용)의 새로운 인스턴스를 만들고 포인터를 통과하십시오.
다른 팁
C ++에서는 스택, malloc/free, new/delete 또는 기타 사용자 정의 구현과 같은 메커니즘을 할당/무료 메모리를 사용할 수 있습니다. 유일한 요구 사항은 하나의 메커니즘으로 메모리 블록을 할당하면 동일한 메커니즘으로 자유롭게해야하므로 전화를 걸 수 없다는 것입니다. free
스택 변수에서 호출 할 수 없습니다 delete
~에 malloc
에드 메모리.
JNI는 JVM 메모리를 할당/해제하기위한 자체 메커니즘을 가지고 있습니다.
- NewObject/deletelocalref
- Newglobalref/deleteglobalref
- Newweakglobalref/deleteweakglobalref
이것들은 동일한 규칙을 따릅니다. 유일한 캐치는 로컬 심판을 명시 적으로 "en en masse"로 삭제할 수 있다는 것입니다. PopLocalFrame
, 기본 메소드가 종료 될 때, 또는 암시 적으로.
JNI는 메모리를 어떻게 할당했는지 알지 못하므로 기능이 종료 될 때는 무료로 메모리를 할 수 없습니다. 여전히 C ++를 작성하기 때문에 스택 변수가 분명히 파괴되지만 GPU 메모리는 유효합니다.
그런 다음 유일한 문제는 후속 호출에서 메모리에 액세스하는 방법입니다. 그런 다음 Gunslinger47의 제안을 사용할 수 있습니다.
JNIEXPORT jlong JNICALL Java_MyJavaClass_Function1() {
MyClass* pObject = new MyClass(...);
return (long)pObject;
}
JNIEXPORT void JNICALL Java_MyJavaClass_Function2(jlong lp) {
MyClass* pObject = (MyClass*)lp;
...
}
Java는 포인터로 무엇을 해야할지 알지 못하지만 기본 기능의 리턴 값에서 포인터를 저장 한 다음 다른 기본 기능으로 나눠 줄 수 있어야합니다. C 포인터는 코어에서 숫자 값에 지나지 않습니다.
또 다른 컨디셔너는 JNI 호출 사이에 그래픽 메모리가 지적되는지 여부와 작업 어라운드가 있을지 여부를 알려야합니다.
이 질문이 이미 공식적으로 답변되었음을 알고 있지만 솔루션을 추가하고 싶습니다. 포인터를 전달하는 대신 포인터를 Java 어레이 (색인 0)에 넣고 JNI로 전달하십시오. JNI 코드는 사용하여 배열 요소를 가져 와서 설정할 수 있습니다. GetIntArrayRegion
/SetIntArrayRegion
.
내 코드에서는 파일 디스크립터 (열린 소켓)를 관리하려면 기본 레이어가 필요합니다. Java 클래스는 a int[1]
배열 및 기본 기능으로 전달합니다. 기본 함수는 무엇이든 수행 할 수 있으며 (GET/SET) 결과를 배열에 다시 넣을 수 있습니다.
기본 기능 내부의 메모리를 동적으로 (힙에) 할당하는 경우 삭제되지 않습니다. 다시 말해, 포인터, 정적 VAR 등을 사용하여 다른 호출 사이의 상태를 기본 함수로 유지할 수 있습니다.
다른 방식으로 생각하십시오 : 다른 C ++ 프로그램에서 불리는 기능 호출을 안전하게 유지할 수 있습니까? 여기에도 같은 것이 적용됩니다. 함수가 종료되면 해당 함수 호출에 대한 스택의 모든 것이 파괴됩니다. 그러나 명시 적으로 삭제하지 않으면 힙의 모든 것이 유지됩니다.
짧은 답변 : 호출 함수로 돌아 오는 결과를 처리하지 않는 한 나중에 재 입력에 유효합니다. 완료되면 정리하십시오.
@denis-tulskiy의 수락 된 답변은 의미가 있지만, 나는 개인적으로 제안을 따랐습니다. 여기.
따라서 의사 포인터 유형을 사용하는 대신 jlong
(또는 jint
32bits 아치에 공간을 절약하려면 대신 사용하십시오. ByteBuffer
. 예를 들어:
MyNativeStruct* data; // Initialized elsewhere.
jobject bb = (*env)->NewDirectByteBuffer(env, (void*) data, sizeof(MyNativeStruct));
나중에 재사용 할 수 있습니다.
jobject bb; // Initialized elsewhere.
MyNativeStruct* data = (MyNativeStruct*) (*env)->GetDirectBufferAddress(env, bb);
매우 간단한 경우이 솔루션은 사용하기가 매우 쉽습니다. 당신이 가지고 있다고 가정합니다.
struct {
int exampleInt;
short exampleShort;
} MyNativeStruct;
Java 측에서는 단순히 다음과 같이해야합니다.
public int getExampleInt() {
return bb.getInt(0);
}
public short getExampleShort() {
return bb.getShort(4);
}
글쓰기에서 벗어나게됩니다 많이 보일러 플레이트 코드! 그러나 설명 된대로 바이트 주문에주의를 기울여야합니다. 여기.
이 작업을 수행하는 것이 가장 좋습니다.
객체를 만들면 32/64 비트 서명 된 정수 인 (UINTPTR_T)에 입력하십시오.
return (uintptr_t) malloc(50);
void * f = (uintptr_t) jlong;
이것이 유일한 올바른 방법입니다.
안전하지 않은 정신 검사는 다음과 같습니다.
inline jlong addr_to_java(void* p) {
assert(p == (void*)(uintptr_t)p, "must not be odd high bits");
return (uintptr_t)p;
}
UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory(JNIEnv *env, jobject unsafe, jlong size))
UnsafeWrapper("Unsafe_AllocateMemory");
size_t sz = (size_t)size;
if (sz != (julong)size || size < 0) {
THROW_0(vmSymbols::java_lang_IllegalArgumentException());
}
if (sz == 0) {
return 0;
}
sz = round_to(sz, HeapWordSize);
void* x = os::malloc(sz, mtInternal);
if (x == NULL) {
THROW_0(vmSymbols::java_lang_OutOfMemoryError());
}
//Copy::fill_to_words((HeapWord*)x, sz / HeapWordSize);
return addr_to_java(x);
UNSAFE_END