JNI를 통해 C와 Java간에 포인터 전달
현재 CUDA 기능을 사용하는 Java 응용 프로그램을 만들려고합니다. CUDA와 Java 간의 연결은 정상적으로 작동하지만 다른 문제가 있으며 이에 대한 내 생각이 올바른지 묻고 싶습니다.
Java에서 기본 함수를 호출 할 때 일부 데이터를 전달하면 함수가 무언가를 계산하고 결과를 반환합니다. 첫 번째 함수가이 결과에 대한 참조 (포인터)를 반환하도록하여 JNI에 전달할 수 있고 결과로 추가 계산을 수행하는 다른 함수를 호출 할 수 있습니까?
제 생각은 데이터를 GPU 메모리에 남겨두고 다른 함수가 사용할 수 있도록 데이터를 GPU 메모리에 그대로 두어 GPU에서 데이터를 복사 할 때 발생하는 오버 헤드를 줄이는 것이 었습니다.
얼마 동안 시도한 후, 응용 프로그램이 종료 된 후 포인터가 삭제되기 때문에 가능하지 않을 것이라고 생각했습니다 (이 경우 C 기능이 종료 될 때). 이 올바른지? 아니면 해결책을보기 위해 C에서 나쁘나요?
편집 : 글쎄, 질문을 조금 확장하려면 (또는 더 명확하게) : JNI 네이티브 함수에 의해 할당 된 메모리는 함수가 끝날 때 할당 해제됩니까? 아니면 JNI 애플리케이션이 종료되거나 수동으로 해제 할 때까지 계속 액세스 할 수 있습니까?
귀하의 의견에 감사드립니다 :)
다음 접근 방식을 사용했습니다.
JNI 코드에서 필요한 객체에 대한 참조를 보유하는 구조체를 만듭니다. 이 구조체를 처음 만들 때 java에 대한 포인터를 long
. 그런 다음 Java에서 this long
를 매개 변수로 사용하여 메서드를 호출 하고 C에서 구조체에 대한 포인터로 캐스팅합니다.
구조는 힙에 있으므로 서로 다른 JNI 호출간에 지워지지 않습니다.
편집 : (long)&address;
주소가 정적 변수이기 때문에 긴 ptr = 사용할 수 있다고 생각하지 않습니다 . Gunslinger47이 제안한 방식으로 사용합니다. 즉, 클래스 또는 구조체의 새 인스턴스를 만들고 (new 또는 malloc 사용) 포인터를 전달합니다.
C ++에서는 스택, malloc / free, new / delete 또는 기타 사용자 정의 구현과 같이 메모리를 할당 / 해제하려는 모든 메커니즘을 사용할 수 있습니다. 유일한 요구 사항은 당신이 하나의 메커니즘으로 메모리 블록을 할당하는 경우, 당신은 당신이 호출 할 수 있도록, 같은 메커니즘을 확보해야한다는 것입니다 free
스택 변수에 당신이 호출 할 수 없습니다 delete
에 malloc
에드 메모리.
JNI에는 JVM 메모리 할당 / 해제를위한 자체 메커니즘이 있습니다.
- NewObject / DeleteLocalRef
- NewGlobalRef / DeleteGlobalRef
- NewWeakGlobalRef / DeleteWeakGlobalRef
이것들은 동일한 규칙을 따르며 유일한 캐치는 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;
...
}
자바는 포인터로 무엇을해야하는지 알지 못하지만, 네이티브 함수의 반환 값에서 포인터를 저장 한 다음 처리 할 다른 네이티브 함수로 전달할 수 있어야합니다. C 포인터는 핵심의 숫자 값에 지나지 않습니다.
다른 contibutor는 그래픽 메모리를 가리키는 것이 JNI 호출 사이에 지워질 지 여부와 해결 방법이 있는지 여부를 알려야합니다.
이 질문에 이미 공식적으로 대답 한 것을 알고 있지만 솔루션을 추가하고 싶습니다. 포인터를 전달하는 대신 포인터를 Java 배열 (인덱스 0)에 놓고 JNI에 전달합니다. JNI 코드는 GetIntArrayRegion
/를 사용하여 배열 요소를 가져오고 설정할 수 있습니다 SetIntArrayRegion
.
내 코드에서 파일 설명자 (오픈 소켓)를 관리하려면 네이티브 레이어가 필요합니다. Java 클래스는 int[1]
배열을 보유하고 이를 원시 함수에 전달합니다. 네이티브 함수는 그것으로 무엇이든 할 수 있고 (get / set) 그 결과를 배열에 되돌릴 수 있습니다.
원시 함수 내에서 동적으로 (힙에) 메모리를 할당하는 경우 삭제되지 않습니다. 즉, 포인터, 정적 변수 등을 사용하여 네이티브 함수에 대한 서로 다른 호출간에 상태를 유지할 수 있습니다.
다른 방식으로 생각해보십시오. 다른 C ++ 프로그램에서 호출 된 함수 호출에서 안전하게 무엇을 유지할 수 있습니까? 여기에도 같은 것이 적용됩니다. 함수가 종료되면 해당 함수 호출에 대한 스택의 모든 항목이 삭제됩니다. 그러나 힙의 모든 것은 명시 적으로 삭제하지 않는 한 유지됩니다.
짧은 대답 : 호출 함수로 반환하는 결과를 할당 해제하지 않는 한 나중에 다시 입력 할 수 있도록 유효합니다. 완료되면 정리하십시오.
@ denis-tulskiy의 대답은 의미가 있지만 여기 에서 제안을 개인적으로 따랐 습니다 .
따라서 jlong
(또는 jint
32 비트 아치에서 약간의 공간을 절약하려는 경우) 와 같은 의사 포인터 유형을 사용하는 대신 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);
}
많은 상용구 코드 를 작성하지 않아도됩니다! 그러나 여기에 설명 된대로 바이트 순서에주의를 기울여야합니다 .
Unsafe.allocateMemory가 수행하는 방식을 정확히 수행하는 것이 가장 좋습니다.
개체를 만든 다음 32/64 비트 부호없는 정수인 (uintptr_t)에 입력합니다.
return (uintptr_t) malloc(50);
void * f = (uintptr_t) jlong;
이것이 유일한 올바른 방법입니다.
Here is the sanity checking Unsafe.allocateMemory does.
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
참고URL : https://stackoverflow.com/questions/1632367/passing-pointers-between-c-and-java-through-jni
'Development Tip' 카테고리의 다른 글
jQuery getJSON 결과를 변수에 저장 (0) | 2020.11.08 |
---|---|
대규모 .NET 프로젝트에서 다국어 / 세계화를 구현하는 가장 좋은 방법 (0) | 2020.11.08 |
Array.length = 0과 Array = []의 차이점은 무엇입니까? (0) | 2020.11.08 |
여러 작업을위한 동일한 작업 공간 (0) | 2020.11.08 |
Python "from [dot] package import…"구문 (0) | 2020.11.08 |