ByteBuffer keinen Arbeitsspeicher freigibt
Auf Android, eine direkte ByteBuffer nicht immer scheinen lassen seine Erinnerung, auch nicht beim aufrufen von System.gc().
Beispiel: tut
Log.v("?", Long.toString(Debug.getNativeHeapAllocatedSize()));
ByteBuffer buffer = allocateDirect(LARGE_NUMBER);
buffer=null;
System.gc();
Log.v("?", Long.toString(Debug.getNativeHeapAllocatedSize()));
gibt zwei zahlen in der log, die zweite wird mindestens GROSSE_ZAHL größer als der erste.
Wie kann ich loswerden dieser Speicherverlust?
Hinzugefügt:
Folgenden der Vorschlag von Gregory zu handhaben alloc/frei auf der C++ - Seite habe ich dann definiert
JNIEXPORT jobject JNICALL Java_com_foo_bar_allocNative(JNIEnv* env, jlong size)
{
void* buffer = malloc(size);
jobject directBuffer = env->NewDirectByteBuffer(buffer, size);
jobject globalRef = env->NewGlobalRef(directBuffer);
return globalRef;
}
JNIEXPORT void JNICALL Java_com_foo_bar_freeNative(JNIEnv* env, jobject globalRef)
{
void *buffer = env->GetDirectBufferAddress(globalRef);
free(buffer);
env->DeleteGlobalRef(globalRef);
}
Ich bekomme dann meine ByteBuffer auf der JAVA-Seite mit
ByteBuffer myBuf = allocNative(LARGE_NUMBER);
und kostenlos mit
freeNative(myBuf);
Leider, während es nicht zuordnen in Ordnung, es a) noch immer hält der Speicher nach Debug.getNativeHeapAllocatedSize()
und b) zu einem Fehler führt
W/dalvikvm(26733): JNI: DeleteGlobalRef(0x462b05a0) failed to find entry (valid=1)
Jetzt bin ich gründlich verwirrt, ich dachte, dass ich das zumindest verstanden die C++ - Seite der Dinge... Warum ist free() nicht zurückgegeben Speicher? Und was mache ich falsch mit der DeleteGlobalRef()
?
- allocateDirect Speicher zuweist, die garantiert nicht von der garbage Collection freigegeben.
- Das ist in Ordnung, aber kann ich frei von der hand, irgendwie? Scheint dumm zu Funktionen, Speicher aber nicht in der Lage sein zu tun ist, die inverse. Ich bin zufrieden mit der Freigabe von JNI, wenn das ist eine option.
- Jon >
allocateDirect
Speicher zuweist, dass ist garantiert verlegt durch den garbage collector. Aber sobald dieByteBuffer
wird gesammelt, der native Speicher zurückgefordert. - siehe meine bearbeitete Antwort über
NewGlobalRef
undDeleteGlobalRef
- so? wo sind Sie mit diesem?
- mögliche Duplikate von Wenn System.gc() alles tun
Du musst angemeldet sein, um einen Kommentar abzugeben.
Gibt es keine Lecks.
ByteBuffer.allocateDirect()
Speicher zuweist, von der native heap /free-Shop (denkemalloc()
), die wiederum verpackt in einerByteBuffer
Instanz.Wenn die
ByteBuffer
Instanz wird Müll gesammelt, der native Speicher zurückgefordert (sonst würden Sie Leck native Speicher).Die Sie aufrufen
System.gc()
in der Hoffnung, den nativen Speicher zurückgefordert sofort. Allerdings aufrufenSystem.gc()
ist nur eine Anfrage, die erklärt, warum Ihre zweite log-Anweisung nicht sagen, Sie Speicher freigegeben wurde: es ist, weil es noch nicht!In Ihrer situation, gibt es anscheinend genug freier Speicher in den Java-heap und den garbage collector entscheidet, nichts zu tun: als eine Folge, nicht erreichbar
ByteBuffer
Instanzen nicht erhoben, doch der finalizer ist nicht ausgeführt und systemeigenen Speicher wird nicht freigegeben.Beachten Sie auch, diese bug in der JVM (nicht sicher, wie es gilt für Dalvik obwohl), wo schwere Zuordnung der direkten Puffer führt zu einem unwiederbringlichen
OutOfMemoryError
.Sie kommentierte zu tun, das controlling, die Dinge aus JNI. Dies ist tatsächlich möglich, Sie könnten die folgenden Maßnahmen:
veröffentlichen
native ByteBuffer allocateNative(long size)
Eintrag zeigen Sie, dass:void* buffer = malloc(size)
reservieren native SpeicherByteBuffer
Beispiel mit einem Aufruf(*env)->NewDirectByteBuffer(env, buffer, size);
wandelt dieByteBuffer
lokale Referenz auf eine Globale mit(*env)->NewGlobalRef(env, directBuffer);
veröffentlichen
native void disposeNative(ByteBuffer buffer)
Eintrag zeigen Sie, dass:free()
auf der direkten Puffer-Adresse zurückgegeben*(env)->GetDirectBufferAddress(env, directBuffer);
löscht das global mit ref(*env)->DeleteGlobalRef(env, directBuffer);
Sobald Sie rufen
disposeNative
auf die Puffer, die man nicht verwenden sollte die Referenz nicht mehr, so könnte es sehr fehleranfällig ist. Überdenken, ob Sie wirklich brauchen eine solche explizite Kontrolle über die Zuweisung Muster.Vergessen, was ich sagte über Globale Referenzen. Eigentlich Globale Referenzen sind eine Möglichkeit, um zu speichern eine Referenz, die in nativem code (wie in einer globalen Variablen), so dass ein weiterer Aufruf von JNI-Methoden verwenden können, die einen Verweis. So hätten Sie zum Beispiel:
foo()
schafft einen globalen Verweis aus einem lokalen Bezug (die durch anlegen eines Objekts aus nativen Seite) und speichert diese in einem einheitlichen globalen variable (alsjobject
)bar()
was bekommt derjobject
gespeichertfoo()
und weitere Prozesse esbaz()
löscht die Globale ReferenzSorry für die Verwirrung.
War ich mit TurqMage Lösung, bis ich es getestet auf einem Android-emulator 4.0.3 (Ice Cream Sandwich). Aus irgendeinem Grund, den Anruf zu DeleteGlobalRef schlägt mit einem jni Achtung: JNI WARNUNG: DeleteGlobalRef auf nicht-Globale 0x41301ea8 (Typ=1), gefolgt von einem "segmentation fault".
Nahm ich die Aufrufe zum erstellen einer NewGlobalRef und DeleteGlobalRef (siehe unten) und es scheint zu funktionieren auf dem Android-4.0.3-emulator.. Wie es sich herausstellt, ich bin nur mit dem erstellten byte-Puffer auf die java-Seite, die sollte halten Sie eine java-Referenz auf es trotzdem, so denke ich, dass der Aufruf NewGlobalRef() wurde nicht benötigt in den ersten Platz..
Nicht sicher, ob deine letzten Kommentare sind alt oder was Kasper. Ich habe die folgenden...
Dann in Java...
und
und alles scheint zu funktionieren gut für mich. Vielen Dank Gregor für diese Idee. Der link zu den referenzierten Bug in der JVM hat gone bad.
Verwenden Sie die Reflexion zu nennen java.nio.DirectByteBuffer.free(). Ich erinnere Sie daran, dass die Android DVM ist inspiriert von Apache Harmony, das unterstützt die oben genannte Methode.
Den direkten NIO Puffer zugewiesen werden, auf die native heap, nicht auf die Java-heap verwaltet der garbage collection. Es liegt an den Entwickler und die Veröffentlichung Ihres nativen Speicher. Es ist ein bisschen anders mit OpenJDK und Oracle Java, da Sie versuchen, den Aufruf der garbage collector, wenn die Herstellung einer direkten NIO Puffer schlägt fehl, aber es gibt keine Garantie, dass es hilft.
N. B: haben Sie zu basteln ein bisschen mehr, wenn Sie verwenden asFloatBuffer(), asIntBuffer (), ..., weil nur die direkten byte-Puffer werden kann "befreit".