10 Stimmen

JNI EnsureLocalCapacity -- WARUM?

Werfen Sie einen Blick auf die JNI-Dokumente hier: http://download.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html

Schauen Sie sich insbesondere an, was in der Beschreibung der Funktion EnsureLocalCapacity gesagt wird:

Aus Gründen der Abwärtskompatibilität weist die VM lokale Referenzen über die gesicherte Kapazität. (Als Unterstützung bei der Fehlersuche kann die VM die Benutzer Warnungen ausgeben, dass zu viele lokale Referenzen erstellt werden. Im JDK kann der Programmierer die Befehlszeilenoption -verbose:jni angeben, um diese Meldungen einschalten). Die VM ruft FatalError auf, wenn keine weiteren lokalen Referenzen über die gesicherte Kapazität hinaus erstellt werden können.

Und sehen Sie sich außerdem an, wie PushLocalFrame ein "Kapazitäts"-Argument entgegennimmt. (Übrigens wird nicht erwähnt, ob es sich dabei um eine harte Grenze oder eine weiche Grenze wie bei EnsureLocalCapacity handelt).

Woher kommt dieser ganze Unsinn über die lokale Referenzkapazität? In den Unterlagen steht, dass VM bereit ist, Referenzen über die derzeitige formale Kapazität hinaus zuzuweisen. Warum also tut es das nicht einfach und hält dieses ganze Kapazitätswirrwarr aus der API heraus?

Um eine Analogie zu C zu ziehen: Es fühlt sich an, als würde man von mir verlangen, im Voraus zu planen, wie viele malloc()-Aufrufe ich machen werde, und das fühlt sich ein bisschen lächerlich an.

Gibt es etwas Wichtiges, das ich hier nicht sehe?

1voto

Tomasz Jarosik Punkte 359

Meinem Verständnis nach ist die Größe der lokalen Referenztabelle vordefiniert, und wenn Sie mehr als das zuweisen, wird es einfach abstürzen. Es ist also nicht so, dass VM Ihnen immer mehr lokale Referenztabellen zur Verfügung stellen wird. Ich habe dies viele Male bei der Arbeit mit JNI auf Android begegnet. Es ist oft schwierig, ein solches Speicherleck zu finden. Und der Aufruf von env->DeleteLocalRef() jedes Mal, wenn man etwas mit lokalen JNI-Referenzen macht, macht den Code nur unübersichtlich. Anstatt also den Code mit DeleteLocalRef()-Aufrufen zu überladen, können wir einfach sicherstellen, dass wir nicht zu viele Referenzen erstellen und den Code recht sauber halten. Und selbst wenn wir planen, viel mehr lokale Referenzen zuzuweisen, können wir immer noch einfach PushLocalFrame()/PopLocalFrame() aufrufen und viele Aufrufe von DeleteLocalRef() vermeiden.

Um Ihnen eine Analogie zu "C" zu geben, werden Sie gebeten, Ihre Aufrufe an malloc im Voraus zu planen, um alle notwendigen Aufrufe an free() am Ende zu vermeiden.

1voto

Wheezil Punkte 2845

Die JNI-Dokumentation ist zu diesem Thema schwammig. In der Praxis habe ich festgestellt, dass die Anzahl der lokalen Referenzen manchmal unbegrenzt zu sein scheint, aber man kann wirklich keine lokalen Referenzen mehr haben, z.B. wenn man mit JNI-Aufrufen über ein Array iteriert. Was ist eine lokale Referenz? Es ist ein Jobject (oder jarray, jstring, etc.), das Sie aus einer dieser Quellen erhalten:

  • Zurückgegeben von einem rohen JNI-Aufruf wie NewObjectV() oder GetObjectArrayElement()
  • Zurückgegeben von einem Methodenaufruf wie CallObjectMethodV()
  • Übergabe an Ihre native Funktion als Argument
  • Wahrscheinlich habe ich andere vergessen

Von diesen müssen Sie sich in der Regel nicht um die Verweise kümmern, die Ihnen in einem Methodenargument übergeben werden, es sei denn, Sie beabsichtigen, den Verweis über den aktuellen Methodenaufruf hinaus beizubehalten; in diesem Fall sollten Sie ihn in global umwandeln.

Es ist zu beachten, dass Sie sich nicht an eine lokale Referenz außerhalb des aktuellen Methodenbereichs hängen dürfen. Wenn Sie sie in einem C++-Objekt speichern wollen, müssen Sie sie in eine globale Referenz umwandeln. Lokale Referenzen haben zwei Vorteile

  1. Sie werden immer automatisch freigegeben, wenn der aktuelle Rahmen den Rahmen verlässt.
  2. Sie sind schneller als die Umstellung auf globale

Wie bei den meisten JNI-Fehlern ist auch der Missbrauch von Referenzen schwer zu diagnostizieren, so dass Sie diesen Fehler nicht haben wollen. Ihre beste Verteidigung ist ein konsistenter Ansatz. Meiner Erfahrung nach sind die Kosten für die Konvertierung von lokal zu global ziemlich gering. Ich würde das Folgende vorschlagen, es sei denn, Sie haben sehr strenge Leistungsanforderungen.
In Anbetracht dessen lautet meine Regel: Konvertieren Sie lokale Referenzwerte IMMER in globale Referenzwerte, indem Sie Code wie diesen verwenden:

jobject localjo = ...
jobject globaljo = env->NewGlobalRef(localjo);
env->DeleteLocalRef(localjo);

An diesem Punkt können Sie wissen, dass jede Referenz global ist, und wenn Sie fertig sind, müssen Sie daran denken, env->DeleteGlobalRef(globaljo) für jede einzelne Referenz auszuführen. In C++ ist es möglich, eine Wrapper-Klasse um JNI-Referenzen herum zu schreiben, die env->DeleteGlobalRef() im Dtor aufruft. Aber da JNI die JNI-Referenzen nicht für Sie referenziert, müssen Sie das möglicherweise auch einbauen.

0voto

18446744073709551615 Punkte 15274

Dies ist mein erraten . Ich vermute, dass die Vergrößerung der lokalen Referenzkapazität teuer ist und diese Funktion es ermöglicht, die gewünschte Kapazität einmal pro nativen Methodenaufruf anzufordern. Ich denke, es geht hier mehr um Leistung als um Korrektheit.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X