how - java memory management
Ejemplos de forzar la liberaciĆ³n de memoria nativa directa que ByteBuffer ha asignado, usando sun.misc.Unsafe? (3)
JDK proporciona una capacidad para asignar los llamados ByteBuffers directos, donde la memoria se asigna fuera del montón de Java. Esto puede ser beneficioso, ya que el recolector de basura no toca esta memoria y, como tal, no contribuye a la sobrecarga del GC: es muy útil para las propiedades de cosas de larga vida como los cachés.
Sin embargo, hay un problema crítico con la implementación existente: la memoria subyacente solo se asigna de forma asíncrona cuando el ByteBuffer propietario se recolecta como basura; no hay manera de forzar una desasignación temprana. Esto puede ser problemático ya que el ciclo del GC en sí no se ve afectado por el manejo de ByteBuffers, y dado que es probable que los ByteBuffers residan en el área de memoria de la Antigua Generación, es posible que se llame al GC horas después de que ByteBuffer ya no esté en uso.
Pero en teoría, debería ser posible usar los métodos sun.misc.Unsafe
(freeMemory, allocateMemory) directamente: esto es lo que JDK utiliza para asignar / desasignar memoria nativa. En cuanto al código, una posible preocupación que veo es la posibilidad de una doble liberación de memoria, por lo que me gustaría asegurarme de que ese estado se limpie correctamente.
¿Alguien me puede indicar el código que hace esto? Lo ideal sería utilizar esto en lugar de JNA.
NOTA: Vi esta pregunta que está relacionada.
Parece que las respuestas indicadas son una buena manera de proceder: here está el ejemplo de código de Elastic Search que usa la idea. Gracias a todos!
Básicamente, lo que desea es seguir la misma semántica que utiliza las secuencias de IO. Al igual que necesita cerrar una secuencia una vez, necesita liberar la memoria una vez. Por lo tanto, puede escribir su propio envoltorio alrededor de las llamadas nativas haciendo posible la liberación temprana de memoria
El uso de sun.misc.Unsafe
es casi imposible porque la dirección base de la memoria nativa asignada es una variable local del constructor java.nio.DirectByteBuffer
.
En realidad, puede forzar la liberación de memoria nativa con el siguiente código:
import sun.misc.Cleaner;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
...
public static void main(String[] args) throws Exception {
ByteBuffer direct = ByteBuffer.allocateDirect(1024);
Field cleanerField = direct.getClass().getDeclaredField("cleaner");
cleanerField.setAccessible(true);
Cleaner cleaner = (Cleaner) cleanerField.get(direct);
cleaner.clean();
}
Hay una forma mucho más sencilla de limpiar la memoria.
public static void clean(ByteBuffer bb) {
if(bb == null) return;
Cleaner cleaner = ((DirectBuffer) bb).cleaner();
if (cleaner != null) cleaner.clean();
}
Su uso puede hacer una gran diferencia si está descartando ByteBuffer directo o con memoria asignada con bastante rapidez.
Una de las razones para usar el limpiador para hacer esto es que puede tener varias copias de los recursos de memoria subyacentes, por ejemplo, con slice (). y el Limpiador tiene un recuento de recursos de estos.