java linux memory-management io memory-mapped-files

java - Truncar el archivo asignado a la memoria



linux memory-management (2)

Su problema es que está utilizando un método no confiable para cerrar el búfer de bytes mapeado (cien llamadas a System.gc() y System.runFinalization() no le garantizan nada). Lamentablemente, no existe un método confiable en Java API para hacer eso, pero en Sun JVM (y tal vez en algunos otros también) puede usar el siguiente código:

public void unmapMmaped(ByteBuffer buffer) { if (buffer instanceof sun.nio.ch.DirectBuffer) { sun.misc.Cleaner cleaner = ((sun.nio.ch.DirectBuffer) buffer).cleaner(); cleaner.clean(); } }

Por supuesto depende de JVM y debería estar preparado para arreglar su código si Sun alguna vez decide cambiar sun.nio.ch.DirectBuffer o sun.misc.Cleaner de una manera incompatible (pero en realidad no creo que esto vaya a sun.nio.ch.DirectBuffer nunca) ocurrir).

Estoy usando IO asignado a memoria para un archivo de índice, pero el problema es que no puedo cambiar el tamaño del archivo si está casi vacío.

En algún lugar antes:

MappedByteBuffer map = raf.getChannel().map(MapMode.READ_WRITE, 0, 1 << 30); raf.close(); // use map map.force(); map = null;

Cambiar el tamaño:

for (int c = 0; c < 100; c++) { RandomAccessFile raf = new RandomAccessFile(indexFile, "rw"); try { raf.setLength(newLen); if (c > 0) LOG.warn("used " + c + " iterations to close mapped byte buffer"); return; } catch (Exception e) { System.gc(); Thread.sleep(10); System.runFinalization(); Thread.sleep(10); } finally { raf.close(); } }

Cuando uso Windows o Linux de 32 bits, a menudo tengo el problema de falta de mapeo, pero en el entorno de producción Linux de 64 bits todo parece funcionar sin advertencias, pero el archivo conserva el tamaño original.

¿Alguien puede explicar por qué sucede esto y / o cómo resolver el problema?


Esto es solo un suplemento de la respuesta anterior, que es completamente correcto.

JDK 1.7 se queja sobre el uso de sun.misc.Cleaner , diciendo que las clases en este espacio de nombres no son una parte formal del JDK, y pueden desaparecer en el futuro. Sin embargo, a partir del 1.7 todavía están presentes.

Si el método .clean() no está disponible, se puede usar System.gc() como método alternativo, sin embargo, se debe reconocer que es un "truco" y, por lo tanto, se debe tener cuidado.

Si bien System.gc() no puede obligar a cerrar un mapeo sin referencia, en la práctica a menudo hará que se System.gc() limpieza. La experiencia en Linux de 32 bits (y Solaris) muestra los búferes que se liberan durante cada prueba durante la primera o la segunda llamada a System.gc() . Sin embargo, el comportamiento en Windows es diferente. En la mayoría de los casos, todas las asignaciones se liberan al final de la segunda llamada a System.gc() , pero a veces requiere 3 llamadas. Todavía hay ocasiones en que se requieren más llamadas, con el requisito de que disminuya la frecuencia de llamadas. Esto puede ser engañoso, ya que las pruebas pueden indicar que 4 llamadas son todo lo que se requiere, solo para que falle un mes después. 5 llamadas pueden parecer adecuadas, solo para conducir al fracaso en 6 meses.

La prueba para ver si se ha lanzado un mapa se puede hacer utilizando un bloque try/catch alrededor de FileChannel.truncate() , con un bucle para volver a intentar la operación en caso de falla. El ciclo no puede ser infinito, ya que hay casos patológicos en los que una configuración de montón particular hará que el recolector de basura nunca limpie una asignación. Sin embargo, un bucle de aproximadamente 10 cubrirá casi todos los casos. Si el objeto no se ha ido en ese punto, entonces no irá a ninguna parte y la aplicación tendrá que darse por vencida. Esto puede parecer inadecuado, pero en la práctica, es extremadamente improbable, y solo será un problema en una JVM que no admita limpiadores.