Java obtiene memoria disponible

¿Hay alguna forma de que la memoria restante esté disponible para la JVM en tiempo de ejecución? El caso de uso de esto sería tener servicios web que fallan elegantemente cuando se acercan a sus límites de memoria rechazando nuevas conexiones con un mensaje de error agradable “demasiadas personas usan esto, intente de nuevo más tarde”, en lugar de morir abruptamente con un error OutOfMemory .

Tenga en cuenta que esto no tiene nada que ver con el cálculo / estimación del costo de cada objeto de antemano. En principio, podría estimar cuánta memoria toman mis objetos y rechazar nuevas conexiones basadas en esa estimación, pero eso parece un poco hacky / frágil.

Esta muestra de William Brendel puede ser de alguna utilidad.

EDITAR: Originalmente proporcioné esta muestra (que enlaza con la respuesta de William Brendel sobre otro tema). El creador de ese tema (Steve M) quería crear una aplicación Java multiplataforma. Específicamente, el usuario estaba tratando de encontrar un medio para evaluar los recursos de la máquina en ejecución (espacio en disco, CPU y uso de memoria).

Esta es una transcripción en línea de la respuesta dada en ese tema. Sin embargo, se ha señalado en este tema que no es la solución ideal, a pesar de que mi respuesta se marcó como aceptada.

public class Main { public static void main(String[] args) { /* Total number of processors or cores available to the JVM */ System.out.println("Available processors (cores): " + Runtime.getRuntime().availableProcessors()); /* Total amount of free memory available to the JVM */ System.out.println("Free memory (bytes): " + Runtime.getRuntime().freeMemory()); /* This will return Long.MAX_VALUE if there is no preset limit */ long maxMemory = Runtime.getRuntime().maxMemory(); /* Maximum amount of memory the JVM will attempt to use */ System.out.println("Maximum memory (bytes): " + (maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory)); /* Total memory currently in use by the JVM */ System.out.println("Total memory (bytes): " + Runtime.getRuntime().totalMemory()); /* Get a list of all filesystem roots on this system */ File[] roots = File.listRoots(); /* For each filesystem root, print some info */ for (File root : roots) { System.out.println("File system root: " + root.getAbsolutePath()); System.out.println("Total space (bytes): " + root.getTotalSpace()); System.out.println("Free space (bytes): " + root.getFreeSpace()); System.out.println("Usable space (bytes): " + root.getUsableSpace()); } } } 

El usuario Christian Fries señala que es incorrecto suponer que Runtime.getRuntime().freeMemory() le da la cantidad de memoria que puede asignarse hasta que se produce un error de falta de memoria.

De la documentación , el retorno de firma de Runtime.getRuntime().freeMemory() es como tal:

Devuelve: una aproximación a la cantidad total de memoria actualmente disponible para futuros objetos asignados, medida en bytes.

Sin embargo, el usuario Christian Fries afirma que esta función puede malinterpretarse. Afirma que la cantidad aproximada de memoria que puede asignarse hasta que se produce un error de falta de memoria (la memoria libre) es probable que esté dada por:

 long presumbleFreeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory; 

Con la allocatedMemory dada por:

 long allocatedMemory = (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()); 

La clave aquí es una discrepancia entre el concepto de memoria libre. Una cosa es la memoria que el sistema operativo proporciona a la Máquina Virtual Java. Otro es la cantidad total de bytes que comprende los fragmentos de bloques de memoria que realmente utiliza la Máquina Virtual de Java.

Teniendo en cuenta que la memoria de las aplicaciones Java se gestiona en bloques por la Máquina Virtual Java, la cantidad de memoria libre disponible para la Máquina Virtual Java puede no coincidir exactamente con la memoria disponible para una aplicación Java.

Específicamente, Christian Fries denota el uso de las banderas -mx o -Xmx para establecer la cantidad máxima de memoria disponible para la Máquina Virtual de Java. Él nota las siguientes diferencias de función:

 /* Returns the maximum amount of memory available to the Java Virtual Machine set by the '-mx' or '-Xmx' flags. */ Runtime.getRuntime().maxMemory(); /* Returns the total memory allocated from the system (which can at most reach the maximum memory value returned by the previous function). */ Runtime.getRuntime().totalMemory(); /* Returns the free memory *within* the total memory returned by the previous function. */ Runtime.getRuntime().freeMemory(); 

Christian concluye su respuesta afirmando que Runtime.getRuntime().freeMemory() de hecho devuelve lo que se puede llamar memoria libre presumible; incluso si una futura asignación de memoria no excede el valor devuelto por esa función, si la Máquina Virtual Java aún no ha recibido la porción real de memoria asignada por el sistema host, aún puede producirse un java.lang.OutOfMemoryError .

Al final, el método adecuado para usar tendrá un grado variable de dependencia en los detalles de su aplicación.

Proporciono otro enlace que puede ser útil. Es una pregunta hecha por el usuario Richard Dormand y respondida por stones333 sobre cómo determinar el tamaño predeterminado del montón de Java utilizado.

Nota: todas las respuestas hasta ahora, incluso la aceptada, parecen responder la pregunta diciendo que Runtime.getRuntime().freeMemory() le da la cantidad de memoria que puede asignarse hasta que se produce un error de falta de memoria. Sin embargo: esto está mal.

La cantidad aproximada de memoria que puede asignarse hasta que se produce un error de falta de memoria, es decir, es probable que haya memoria libre

 long presumbleFreeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory; 

dónde

 long allocatedMemory = (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()); 

Explicación: si inicia la JVM a través de un parámetro -mx (o -Xmx), especifica la cantidad máxima disponible para la JVM. Runtime.getRuntime().maxMemory() le dará esta cantidad. A partir de esta cantidad de memoria del sistema, la JVM asignará la memoria en fragmentos, por ejemplo bloques de 64 mb. Al inicio, la JVM solo asignará tal porción del sistema y no la cantidad total. Runtime.getRuntime().totalMemory() proporciona la memoria total asignada desde el sistema, mientras que Runtime.getRuntime().freeMemory() le proporciona la memoria libre dentro de la memoria total asignada.

Por lo tanto:

 long definitelyFreeMemory = Runtime.getRuntime().freeMemory(); 

es la memoria libre ya reservada por la JVM, pero es probable que solo sea una pequeña cantidad. Y es probable que tengas presumbleFreeMemory . Por supuesto, puede obtener una excepción de falta de memoria incluso si intentó asignar una cantidad menor que presumbleFreeMemory . Esto puede suceder si la JVM no obtiene la siguiente porción de memoria del sistema. Sin embargo, en la mayoría de los sistemas, esto nunca sucederá y el sistema comenzará a intercambiar, una situación que le gustaría evitar. Wrt a la pregunta original: if -mx se establece en un valor razonable, entonces presumbleFreeMemory es un buen indicador para la memoria libre.

Además de utilizar los métodos Runtime, puede obtener información de memoria adicional usando

 MemoryMXBean memBean = ManagementFactory.getMemoryMXBean(); MemoryUsage heap = memBean.getHeapMemoryUsage(); MemoryUsage nonheap = memBean.getNonHeapMemoryUsage(); 

Cada MemoryUsage proporciona valores init, usados, comprometidos y máximos. Esto podría ser útil si crea un hilo de monitor de memoria que sondee la memoria y la registre, lo que le proporcionará un historial de uso de la memoria a lo largo del tiempo. A veces es útil ver el uso de memoria a lo largo del tiempo hasta llegar a los errores.

Si realmente quieres llevar esto al extremo, crea un hilo de volcado dynamic. Supervise el uso de su memoria a lo largo del tiempo y cuando exceda ciertos umbrales haga lo siguiente (esto funciona en JBoss 5.0, su millaje puede variar):

 // init code MBeanServer server = ManagementFactory.getPlatformMBeanServer(); HotSpotDiagnosticMXBean diagBean = ManagementFactory.newPlatformMXBeanProxy(server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class); // loop code // add some code to figure if we have passed some threshold, then File heapFile = new File(outputDir, "heap-" + curThreshold + ".hprof"); log.info("Dumping heap file " + heapFile.getAbsolutePath()); diagBean.dumpHeap(heapFile.getAbsolutePath(), true); 

Más tarde puede revisar estos archivos de volcado de almacenamiento dynamic con el analizador de memoria de eclipse o herramientas similares para verificar si hay pérdidas de memoria, etc.

Además de la otra respuesta, me gustaría señalar que hacerlo no es necesariamente una buena idea, ya que puede tener un caché en su aplicación que use SoftReferences .

Tal caché liberaría memoria tan pronto como la JVM scope sus límites de memoria. Asignar memoria, incluso si no hay suficiente memoria libre, primero haría que la memoria se liberara por las referencias suaves, y la haría disponible para la asignación.

Para obtener la memoria disponible para todo el sistema operativo, agregue OSHI utilizando la siguiente dependencia de maven:

  com.github.dblock oshi-core LATEST  

Luego en Java, use el siguiente código:

 SystemInfo si = new SystemInfo(); HardwareAbstractionLayer hal = si.getHardware(); long availableMemory = hal.getMemory().getAvailable(); 

Siempre puede llamar a Runtime.getRuntime().freeMemory() .

La otra mitad del problema, obtener el costo de los objetos, me parece más problemático.

Creo que una mejor solución sería averiguar cómo agrupar y escalar sus servicios web para que puedan aceptar elegantemente el 150% de la carga nominal sin negar nuevas conexiones. Parece que un ejercicio de dimensionamiento te proporcionaría una solución mejor que un código pirateado.

 long freeMemory = Runtime.getRuntime().freeMemory(); 

Runtime.getRuntime().freeMemory() es una forma de obtener memoria libre para JVM en ese momento durante el tiempo de ejecución. Es una buena manera (o) no depende completamente de su aplicación.