Desasignación de memoria nativa de almacenamiento intermedio directo en Java para JOGL

Estoy usando búferes directos (java.nio) para almacenar información de vértices para JOGL. Estos almacenamientos intermedios son grandes y se reemplazan varias veces durante la vida útil de la aplicación. La memoria no está desasignada a tiempo y me estoy quedando sin memoria después de unos pocos reemplazos.

Parece que no hay una buena manera de desasignar el uso de las clases de buffer de java.nio. Mi pregunta es esta:

¿Hay algún método en JOGL para eliminar los búferes directos? Estoy buscando glDeleteBuffer (), pero parece que esto solo elimina el búfer de la memoria de la tarjeta de video.

Gracias

Los búferes directos de NIO usan memoria no administrada. Significa que están asignados en el montón nativo, no en el montón de Java. Como consecuencia, se liberan solo cuando la JVM se queda sin memoria en el montón de Java, no en el montón nativo. En otros términos, no está administrado = depende de usted administrarlos. Se desaconseja forzar la recolección de basura y no resolverá este problema la mayor parte del tiempo.

Cuando sepa que un búfer NIO directo se ha vuelto inútil para usted, debe liberar su memoria nativa utilizando sun.misc.Cleaner (StaxMan tiene razón) y call clean () (excepto con Apache Harmony), call free () (con Apache Harmony) o use una mejor API pública para hacer eso (tal vez en Java> = 1.9, AutoCleaning que extiende AutoCloseable?).

No es tarea de JOGL hacer eso, puedes usar código Java simple para hacerlo tú mismo. Mi ejemplo está bajo GPL v2 y este ejemplo está bajo una licencia más permisiva.

Edición: Mi último ejemplo funciona incluso con Java 1.9 y es compatible con OpenJDK, Oracle Java, Sun Java, Apache Harmony, GNU Classpath y Android. Es posible que deba eliminar algo de azúcar sintáctica para que funcione con Java <1.7 (las capturas múltiples, los diamantes y los genéricos).

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

Los objetos Direct ByteBuffer limpian sus búferes nativos automáticamente, pero solo pueden hacerlo como parte del GC de Java Heap, por lo que no responden automáticamente a la presión en el montón nativo. El GC ocurre solo cuando el montón de Java está tan lleno que no puede dar servicio a una solicitud de asignación de montón o si la aplicación Java lo solicita explícitamente (no se recomienda porque causa problemas de rendimiento).

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

El contenido de los búfers directos puede residir fuera del montón recogido de basura normal

Esta solución (en este JEP , aún un borrador, probablemente no disponible en Java 1.9) es muy prometedora, no necesitaremos utilizar API no públicas.

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 se ha movido a jdk.internal.ref.Cleaner en Java 1.9 en el módulo “java.base” pero este último implementa java.lang.Runnable (gracias a Alan Bateman por recordarme esa diferencia). Luego, solo tiene que convertir el limpiador en Runnable y llamar al método run() (no lo llame por reflexión para evitar obtener java.lang.IllegalAccessException). Funciona, acabo de probar (6 de agosto de 2016) con Java 1.9 Early Access build 129.

Sin embargo, jdk.internal.ref.Cleaner podría moverse a java.ref.Cleaner $ Cleanable más tarde.

Hay un buen ejemplo en Lucene bajo una licencia más permisiva.

Los búferes directos son complicados y no tienen las garantías habituales de recolección de basura. Consulte para obtener más detalles: http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#direct

Si tiene problemas, sugiero asignar una vez y volver a utilizar el búfer en lugar de asignar y desasignar repetidamente.

La desasignación de un búfer directo es un trabajo realizado por el recolector de basura un tiempo después de que se marca el objeto ByteBuffer.

Podría intentar llamar al gc inmediatamente después de eliminar la última referencia a su búfer. Al menos existe la posibilidad de que la memoria se libere un poco más rápido.

La forma en que se realiza la desasignación es horrible: una referencia suave se inserta básicamente en un objeto más limpio, que luego desasigna la propiedad cuando ByteBuffer es basura. Pero esto no está garantizado para ser llamado de manera oportuna.

En lugar de abusar del reflection de apis no públicas, puedes hacer esto trivialmente, completamente dentro de los públicos respaldados.

Escriba algún JNI que envuelva malloc con NewDirectByteBuffer (recuerde establecer el orden) y una función análoga para liberarlo.