Frage

Im Moment versuche ich, eine Java-Anwendung zu erstellen, die CUDA-Funktionalität verwendet. Die Verbindung zwischen CUDA und Java funktioniert gut, aber ich habe ein anderes Problem bekommt und wollte fragen, ob meine Gedanken darüber korrekt sind.

Wenn ich eine native Funktion von Java aufrufen, übergeben ich es einige Daten, berechnet die Funktionen etwas und gibt ein Ergebnis zurück. Ist es möglich, die erste Funktion zurückgeben einen Verweis (Zeiger) zu diesem Ergebnis zu lassen, die ich zu JNI und riefen eine andere Funktion übergeben kann, die weiteren Berechnungen mit dem Ergebnis, das?

war meine Idee, den Aufwand zu reduzieren, indem man die Daten in dem GPU-Speicher zu kopieren Daten zu und von der GPU kommt und nur einen Verweis auf mich, so dass andere Funktionen geben können es verwenden.

einige Zeit Nach dem Versuch, die ich für mich gedacht, soll dies nicht möglich sein, weil Zeiger nach den Anwendungsenden gelöscht werden (in diesem Fall, wenn die C-Funktion beendet). Ist das richtig? Oder bin ich nur schlecht in C die Lösung zu sehen?

Edit: Nun, zu erweitern, um die Frage ein wenig (oder macht es deutlicher): Ist Speicher zugewiesen durch JNI nativen Funktionen freigegeben, wenn die Funktion endet? Oder kann ich immer noch darauf zugreifen, bis entweder die JNI-Anwendung endet oder wenn ich frei, um es manuell?

Vielen Dank für Ihre Eingabe:)

War es hilfreich?

Lösung

Ich habe den folgenden Ansatz:

in Ihrem JNI Code, erstellen Sie eine Struktur, die Verweise auf Objekte halten würden Sie brauchen. Wenn Sie zuerst diese Struktur erstellen, gibt dessen Zeiger auf Java als long. Dann wird aus Java rufen Sie einfach jede Methode mit diesem long als Parameter, und in C warf es auf einen Zeiger auf Ihre Struktur.

Die Struktur in der Halde sein wird, so wird es ruft nicht zwischen verschiedenen JNI gelöscht werden.

EDIT: Ich glaube nicht, dass Sie lange ptr = (long)&address; verwenden können, da Adresse eine statische Variable ist. Verwenden Sie es, die Art und Weise Gunslinger47 vorgeschlagen, das heißt neue Instanz der Klasse erstellen oder eine Struktur (mit neuen oder malloc) und übergeben ihre Zeiger.

Andere Tipps

In C ++ Sie einen beliebigen Mechanismus Sie / freier Speicher zuweisen möchten verwenden können: den Stapel, malloc / free, neu / löschen oder jede andere benutzerdefinierte Implementierung. Die einzige Voraussetzung ist, dass, wenn Sie einen Speicherblock mit einem Mechanismus zugeordnet, können Sie es mit dem gleichen Mechanismus zu befreien, so dass Sie nicht free auf einem Stapel Variable nennen kann, und Sie können nicht delete auf malloced Speicher aufrufen.

JNI hat seine eigenen Mechanismen für die Zuweisung / Freigabe JVM-Speicher:

  • NewObject / DeleteLocalRef
  • NewGlobalRef / DeleteGlobalRef
  • NewWeakGlobalRef / DeleteWeakGlobalRef

Diese dieselbe Regel folgt, ist der einzige Haken, dass lokale Refs „en masse“ gelöscht werden können, entweder explizit mit PopLocalFrame oder implizit, wenn die native Methode beendet wird.

JNI nicht wissen, wie Sie Ihren Speicher zugewiesen, so kann es nicht frei, wenn Ihre Funktion beendet. Stack-Variablen wird offensichtlich zerstört werden, weil Sie immer noch C ++ zu schreiben, aber Ihre GPU-Speicher behalten ihre Gültigkeit.

Das einzige Problem ist dann, wie auf nachfolgenden Aufrufen auf den Speicher zuzugreifen, und dann können Sie Gunslinger47 Vorschlag verwenden:

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 würde nicht wissen, was mit einem Zeiger zu tun, aber es sollte einen Zeiger von einer nativen Funktion Rückgabewert speichern kann, um es dann an einem anderen native Funktion der Hand von für sie zu behandeln. C-Zeiger sind nichts anderes als numerische Werte in den Mittelpunkt.

Ein weiterer contibutor würde Ihnen sagen, ob die zu Grafiken zeigte Speicher zwischen JNI Anrufungen gelöscht würden und ob es irgendwelche Umgehungen wäre.

Ich weiß, diese Frage wurde bereits offiziell beantwortet, aber ich möchte meine Lösung hinzuzufügen: Statt einen Zeiger, zu versuchen, setzte in einem Java-Array um den Zeiger zu übergeben (mit dem Index 0) und übergibt das zu JNI. JNI-Code kann das Array-Element mit GetIntArrayRegion / SetIntArrayRegion erhalten und eingestellt werden.

In meinem Code, ich brauche die native Schicht einen Dateideskriptor zu verwalten (eine offene Buchse). Die Java-Klasse hält eine int[1] Array und übergibt sie an die native Funktion. Die native Funktion kann mit ihr, was tun (get / set) und das Ergebnis in den Array zurückstellen.

Wenn Sie Speicher dynamisch die Zuweisung sind (auf dem Heap) innerhalb der nativen Funktion wird nicht gelöscht. Mit anderen Worten, sind Sie in der Lage Zustand zwischen verschiedenen Anrufen in nativen Funktionen zu behalten, mit Zeigern, statischer Vars, usw.

Denken Sie daran, eine andere Art und Weise: Was könnten Sie sicher in einem Funktionsaufruf verhindere, von einem anderen C ++ Programm namens? Entsprechendes gilt hier. Wenn eine Funktion verlassen, alles auf dem Stapel für diesen Funktionsaufruf wird zerstört; aber alles, was auf dem Heap zurückgehalten wird, es sei denn, Sie explizit löschen.

Kurze Antwort: Solange Sie das Ergebnis nicht tun freigeben Sie an die anrufende Funktion sind Rückkehr, wird es später für den Wieder Eingang gültig bleiben. So stellen Sie sicher, dass es zu bereinigen, wenn Sie fertig sind.

Während die akzeptierte Antwort von @ denis-tulskiy macht Sinn, ich habe persönlich gefolgt Vorschläge von hier .

Statt einen pseudo-Zeigertyp wie jlong der Verwendung (oder jint, wenn Sie etwas Platz auf 32bits Bogen speichern möchten), verwenden Sie stattdessen eine ByteBuffer. Zum Beispiel:

MyNativeStruct* data; // Initialized elsewhere.
jobject bb = (*env)->NewDirectByteBuffer(env, (void*) data, sizeof(MyNativeStruct));

, die Sie später wieder verwenden mit:

jobject bb; // Initialized elsewhere.
MyNativeStruct* data = (MyNativeStruct*) (*env)->GetDirectBufferAddress(env, bb);

Bei sehr einfachen Fällen ist diese Lösung sehr einfach zu bedienen. Angenommen, Sie haben:

struct {
  int exampleInt;
  short exampleShort;
} MyNativeStruct;

Auf der Java-Seite, müssen Sie einfach tun:

public int getExampleInt() {
  return bb.getInt(0);
}

public short getExampleShort() {
  return bb.getShort(4);
}

Was Sie vom Schreiben spart viel Standardcode! Man sollte jedoch die Aufmerksamkeit auf Byteanordnung zahlen als hier erklärt.

Die besten dies genau das zu tun, wie Unsafe.allocateMemory der Fall ist.

Erstellen Sie Ihr Objekt dann geben Sie es zu (uintptr_t), die ein 32/64 Bit unsigned integer ist.

return (uintptr_t) malloc(50);

void * f = (uintptr_t) jlong;

Dies ist der einzig richtige Weg, es zu tun.

Hier ist die geistige Gesundheit Überprüfung Unsafe.allocateMemory der Fall ist.

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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top