Java obtener memoria disponible


¿Hay alguna buena manera de obtener la memoria restante disponible para la JVM en tiempo de ejecución? El caso de uso de esto sería tener servicios web que fallan con gracia cuando se están acercando a sus límites de memoria al rechazar nuevas conexiones con un bonito mensaje de error "demasiada gente usando esto, inténtelo de nuevo más tarde", en lugar de morir abruptamente con un error fuera de memoria.

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

Author: Li Haoyi, 2012-10-10

8 answers

Esta muestra de William Brendel puede ser de alguna utilidad.

EDITAR: Originalmente proporcioné esta muestra (enlazando a 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, ha se ha señalado sobre este tema que no es la solución ideal, a pesar de que mi respuesta está marcada 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 asumir que Runtime.getRuntime().freeMemory() le da la cantidad de memoria que puede ser asignada hasta que ocurra un error de falta de memoria.

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

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

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

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

Siendo allocatedMemory dado 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 el Java Virtual Equipo. Otra es la cantidad total de bytes que comprenden los trozos de bloques de memoria que realmente están siendo utilizados por la propia Máquina Virtual Java.

Teniendo en cuenta que la memoria dada a las aplicaciones Java es administrada 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 Java. Señala 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 asignación de memoria futura no excede el valor devuelto por esa función, si la Máquina Virtual de Java aún no ha recibido el fragmento real de memoria asignado por el sistema host, un java.lang.OutOfMemoryError todavía puede ser producido.

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

Proporciono otro enlace que puede ser útil. Es una pregunta hecha por el usuario Richard Dormand y contestada por stones333 sobre la determinación del tamaño de pila Java por defecto utilizado.

 69
Author: Blitzkoder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:03:05

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

El aproximado cantidad de memoria que se puede asignar hasta que se produce un error de falta de memoria, es decir, la memoria libre es probable

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

Donde

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á memoria en trozos, por ejemplo, bloques de 64 mb. Al principio, la JVM solo asignará un fragmento del sistema y no la cantidad total. Runtime.getRuntime().totalMemory() da la memoria total asignada del sistema, mientras que Runtime.getRuntime().freeMemory()le da la memoria libre dentro de la memoria total asignada.

Por lo tanto:

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

Es la memoria libre ya reservado por la JVM, pero es probable que solo una pequeña cantidad. Y probablemente obtendrá presumableFreeMemory. Por supuesto, puede obtener una excepción fuera de memoria incluso si intentó asignar una cantidad menor que presumableFreeMemory. Esto puede suceder si la JVM no obtiene el siguiente fragmento 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. W. r. t. a la pregunta original: si-mx se establece en un valor razonable, entonces presumableFreeMemory es una buena indicador de la memoria libre.

 55
Author: Christian Fries,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-25 10:30:32

Además de usar los métodos de tiempo de ejecución, puede obtener información de memoria adicional utilizando

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

Cada MemoryUsage proporciona valores init, used, committed y max. Esto podría ser útil si crea un subproceso de monitor de memoria que sondee la memoria y la registre, proporcionándole un historial de uso de memoria a lo largo del tiempo. A veces es útil ver el uso de memoria a lo largo del tiempo que conduce a los errores.

Si realmente quieres llevar esto a un extremo, crea un volcado de montón hilo. Monitoree su uso de memoria a lo largo del tiempo y cuando supere ciertos umbrales haga lo siguiente (esto funciona en JBoss 5.0 - su kilometraje 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 pilas con eclipse memory analyzer o herramientas similares para verificar fugas de memoria, etc.

 7
Author: Guido Simone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-09 23:11:03

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

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

 5
Author: JB Nizet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-09 20:25:22

Para obtener la memoria disponible en todo el sistema operativo, agregue OSHI usando la siguiente dependencia maven:

<dependency>
    <groupId>com.github.dblock</groupId>
    <artifactId>oshi-core</artifactId>
    <version>LATEST</version>
</dependency>

Luego, en Java, use el siguiente código:

SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
long availableMemory = hal.getMemory().getAvailable();
 5
Author: Oleksii Kyslytsyn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-28 21:26:25

Siempre puedes 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 con gracia el 150% de la carga nominal sin negar nuevas conexiones. Parece que un ejercicio de dimensionamiento te daría una mejor solución que un hackeo de código.

 0
Author: duffymo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-09 20:21:27
long freeMemory = Runtime.getRuntime().freeMemory();
 0
Author: Ryan Anderson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-09 20:22:38

Runtime.getRuntime().freeMemory() es una forma de obtener memoria libre para JVM en ese momento mientras se ejecuta. ¿Es buena manera (o) no depende completamente de su aplicación.

 0
Author: kosa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-24 14:05:36