Deallocating memoria nativa del buffer diretto in Java per JOGL

Sto usando i buffer diretti (java.nio) per memorizzare le informazioni sui vertici per JOGL. Questi buffer sono grandi e vengono sostituiti più volte durante la vita dell’applicazione. La memoria non è stata rilasciata nel tempo e sto esaurendo la memoria dopo alcune sostituzioni.

Sembra che non ci sia un buon modo per deallocare usando le classi buffer di java.nio. La mia domanda è questa:

C’è qualche metodo in JOGL per eliminare i buffer diretti? Sto cercando in glDeleteBuffer (), ma sembra che questo cancelli solo il buffer dalla memoria della scheda video.

Grazie

I buffer NIO diretti utilizzano memoria non gestita. Significa che sono allocati nell’heap nativo, non nell’heap Java. Di conseguenza, vengono liberati solo quando la JVM esaurisce la memoria sull’heap Java, non sull’heap nativo. In altri termini, non è gestito = dipende da te gestirli. Costringere la garbage collection è scoraggiato e non risolverà questo problema il più delle volte.

Quando sai che un buffer NIO diretto è diventato inutile per te, devi rilasciare la sua memoria nativa usando il suo sun.misc.Cleaner (StaxMan ha ragione) e chiama clean () (tranne con Apache Harmony), chiama free () (con Apache Harmony) o utilizzare una migliore API pubblica per farlo (forse in Java> = 1.9, AutoCleaning che estende AutoCloseable?).

Non è compito di JOGL farlo, è ansible utilizzare un semplice codice Java per farlo da soli. Il mio esempio è sotto GPL v2 e questo esempio è sotto una licenza più permissiva.

Edit .: Il mio ultimo esempio funziona anche con Java 1.9 e supporta OpenJDK, Oracle Java, Sun Java, Apache Harmony, GNU Classpath e Android. Potrebbe essere necessario rimuovere dello zucchero sintattico per farlo funzionare con Java <1.7 (le multi-catture, i diamanti e i generici).

Riferimento: http://www.ibm.com/developerworks/library/j-nativememory-linux/

Gli oggetti ByteBuffer diretti cancellano automaticamente i loro buffer nativi, ma possono farlo solo come parte del GC dell’heap Java, in modo che non rispondano automaticamente alla pressione sull’heap nativo. Il GC si verifica solo quando l’heap Java diventa così pieno che non può servire una richiesta di allocazione dell’heap o se l’applicazione Java lo richiede esplicitamente (non consigliato perché causa problemi di prestazioni).

Riferimento: http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#direct

Il contenuto dei buffer diretti può risiedere al di fuori del normale heap spazzato

Questa soluzione (in questo JEP , ancora una bozza, probabilmente non disponibile in Java 1.9) è molto promettente, non avremo bisogno di utilizzare API non pubbliche.

public long memory(long index) { // The scope where the memory region is available // Implements AutoClosable but `close` can be called manually as well try (Scope scope = new NativeScope()) { // Allocate the actual memory area, in this case in the style of a "long-array" Pointer ptr = scope.allocate( NativeLibrary.createLayout(long.class), numElements); // Get the reference to a certain element Reference ref = ptr.offset(index).deref(); // Set a value to this element through the reference ref.set(Long.MAX_VALUE); // Read the value of an element return ref.get(); } } 

NB: sun.misc.Cleaner è stato spostato in jdk.internal.ref.Cleaner in Java 1.9 nel modulo “java.base” ma quest’ultimo implementa java.lang.Runnable (grazie ad Alan Bateman per avermi ricordato questa differenza). Quindi, devi solo lanciare il pulitore su Runnable e chiamare il metodo run() (non chiamarlo per riflessione per evitare di ottenere java.lang.IllegalAccessException). Funziona, l’ho appena testato (6 agosto 2016) con Java 1.9 Early Access build 129.

Tuttavia, jdk.internal.ref.Cleaner potrebbe essere spostato su java.ref.Cleaner $ Pulibile in seguito.

C’è un buon esempio in Lucene con una licenza più permissiva.

I buffer diretti sono difficili e non hanno le solite garanzie di garbage collection – vedi per maggiori dettagli: http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#direct

Se si riscontrano problemi, suggerirei di allocare una volta e riutilizzare il buffer anziché allocarlo e deallocarlo ripetutamente.

La deallocazione di un buffer diretto è un lavoro svolto dal garbage collector dopo che l’object ByteBuffer è stato contrassegnato.

Potresti provare a chiamare il gc immediatamente dopo aver eliminato l’ultimo riferimento nel tuo buffer. Almeno c’è una possibilità che la memoria sarà liberata un po ‘più veloce.

Il modo in cui viene eseguita la deallocation è terribile – un riferimento soft viene fondamentalmente inserito in un object Cleaner, che quindi effettua deallocazione quando possiede ByteBuffer è garbage collection. Ma questo non è davvero garantito per essere chiamato in modo tempestivo.

Piuttosto che abusare della riflessione sugli apis non pubblici, puoi farlo banalmente, interamente all’interno di quelli pubblici supportati.

Scrivi un JNI che avvolge malloc con NewDirectByteBuffer (ricorda di impostare l’ordine) e una funzione analoga per liberarlo.