87 votes

Java obtient la mémoire disponible

Existe-t-il un bon moyen d'obtenir la mémoire restante disponible pour la JVM au moment de l'exécution ? Le cas d'utilisation de ceci serait d'avoir des services web qui échouent gracieusement quand ils approchent de leurs limites de mémoire en refusant les nouvelles connexions avec un message d'erreur sympathique "trop de personnes utilisent ce service, réessayez plus tard", plutôt que de mourir brusquement avec une erreur OutOfMemory.

Notez que cela n'a rien à voir avec le fait de calculer/estimer le coût de chaque objet au préalable. En principe, je pourrais estimer la quantité de mémoire occupée par mes objets et refuser les nouvelles connexions en fonction de cette estimation, mais cela semble un peu compliqué/fragile.

108voto

Blitzkoder Points 1638

Cet échantillon de William Brendel peut vous être utile.

EDIT : J'ai initialement fourni cet échantillon (en faisant un lien vers la réponse de William Brendel sur un autre sujet). Le créateur de ce sujet (Steve M) voulait créer une application Java multi-plateforme. Plus précisément, l'utilisateur essayait de trouver un moyen d'évaluer les ressources de la machine en cours d'exécution (espace disque, utilisation du CPU et de la mémoire).

Il s'agit d'une transcription en ligne de la réponse donnée dans cette rubrique. Cependant, il a été signalé dans ce sujet que ce n'est pas la solution idéale, bien que ma réponse ait été marquée comme acceptée.

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());
  }
 }
}

L'utilisateur Christian Fries souligne qu'il est erroné de supposer que Runtime.getRuntime().freeMemory() vous donne la quantité de mémoire qui peut être allouée jusqu'à ce qu'une erreur de dépassement de mémoire se produise.

De la documentation le retour de signature de Runtime.getRuntime().freeMemory() est comme tel :

Les retours : une approximation de la quantité totale de mémoire actuellement disponible pour les futurs objets alloués, mesurée en octets.

Cependant, l'utilisateur Christian Fries affirme que cette fonction peut être mal interprétée. Il affirme que la quantité approximative de mémoire qui peut être allouée jusqu'à ce qu'une erreur d'out-of-memory se produise (la mémoire libre) est probablement donnée par :

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

Con allocatedMemory étant donné par :

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

La clé ici est une divergence entre le concept de mémoire libre. Une chose est la mémoire que le système d'exploitation fournit à la machine virtuelle Java. Une autre est la quantité totale d'octets comprenant les morceaux de blocs de mémoire réellement utilisés par la machine virtuelle Java elle-même.

Si l'on considère que la mémoire donnée aux applications Java est gérée en blocs par la machine virtuelle Java, la quantité de mémoire libre dont dispose la machine virtuelle Java peut ne pas correspondre exactement à la mémoire disponible pour une application Java.

Plus précisément, Christian Fries dénote l'usage de la -mx o -Xmx pour définir la quantité maximale de mémoire disponible pour la machine virtuelle Java. Il note les différences de fonctions suivantes :

/* 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 conclut sa réponse en affirmant que Runtime.getRuntime().freeMemory() renvoie en fait ce que l'on peut appeler de la mémoire libre présumée ; même si une future allocation de mémoire ne dépasse pas la valeur renvoyée par cette fonction, si la machine virtuelle Java n'a pas encore reçu le morceau de mémoire réel attribué par le système hôte, un message d'erreur java.lang.OutOfMemoryError peuvent encore être produites.

En fin de compte, la méthode à utiliser dépendra à des degrés divers des spécificités de votre application.

Je fournis un autre lien qui peut être utile. Il s'agit d'une question posée par l'utilisateur Richard Dormand et à laquelle stones333 a répondu. sur la détermination de la taille par défaut du tas Java utilisé.

12voto

Oleksii Kyslytsyn Points 1165

Pour obtenir la mémoire disponible pour l'ensemble du système d'exploitation, ajoutez OSHI en utilisant la dépendance maven suivante :

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

Ensuite, en Java, utilisez le code suivant :

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

8voto

Guido Simone Points 4655

En plus d'utiliser les méthodes du Runtime, vous pouvez obtenir des informations supplémentaires sur la mémoire en utilisant

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

Chaque Utilisation de la mémoire fournit les valeurs init, used, committed et max. Cela peut être utile si vous créez un thread de surveillance de la mémoire qui interroge la mémoire et l'enregistre, vous fournissant ainsi un historique de l'utilisation de la mémoire dans le temps. Il est parfois utile de voir l'utilisation de la mémoire dans le temps qui précède les erreurs.

Si vous voulez vraiment aller à l'extrême, créez un fil de vidage de tas. Surveillez l'utilisation de la mémoire au fil du temps et, lorsqu'elle dépasse certains seuils, faites ce qui suit (ceci fonctionne sur JBoss 5.0 - vos habitudes peuvent varier) :

// 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);

Plus tard, vous pourrez revoir ces fichiers de vidage de tas avec l'éclipse. analyseur de mémoire ou des outils similaires pour vérifier les fuites de mémoire, etc.

7voto

JB Nizet Points 250258

En plus de l'autre réponse, je voudrais noter que faire cela n'est pas nécessairement une bonne idée, car vous pouvez avoir un cache dans votre application qui utilise SoftReferences .

Un tel cache libérerait la mémoire dès que la JVM atteint ses limites de mémoire. L'allocation de mémoire, même s'il n'y a pas assez de mémoire libre, entraînerait d'abord la libération de la mémoire par les références souples, et la rendrait disponible pour l'allocation.

1voto

duffymo Points 188155

Vous pouvez toujours appeler Runtime.getRuntime().freeMemory() .

L'autre moitié du problème, l'obtention du coût des objets, me semble plus problématique.

Je pense qu'une meilleure solution serait de trouver comment mettre en grappe et dimensionner vos services Web de manière à ce qu'ils puissent accepter 150 % de la charge nominale sans refuser de nouvelles connexions. Il semblerait qu'un exercice de dimensionnement vous apporterait une meilleure solution qu'un piratage de code.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X