Existe-t-il un moyen efficace de mettre la mémoire disponible à la disposition de la machine virtuelle Java au moment de l'exécution? Le cas d'utilisation de ce 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 Nice "trop de gens l'utilisant, essayez plus tard", plutôt que de mourir brusquement avec une erreur OutOfMemory .
Notez que cela n'a rien à voir avec le calcul/l'estimation préalable du coût de chaque objet. En principe, je pourrais estimer la quantité de mémoire que mes objets utilisent et refuser de nouvelles connexions en fonction de cette estimation, mais cela me semble plutôt hacky/fragile.
Cet échantillon de William Brendel pourrait être utile.
EDIT: J'ai initialement fourni cet échantillon (en lien avec la réponse de William Brendel sur un autre sujet). Le créateur de ce sujet (Steve M) souhaitait créer une application multi-plateforme Java. En particulier, l’utilisateur essayait de trouver un moyen d’évaluer les ressources de la machine en cours d’exécution (espace disque, utilisation du processeur et de la mémoire).
Ceci est une transcription en ligne de la réponse donnée dans cette rubrique. Cependant, il a été souligné à ce sujet que ce n’était pas la solution idéale, malgré le fait 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 fait remarquer qu'il est faux de supposer que Runtime.getRuntime().freeMemory()
vous donne la quantité de mémoire qui peut être allouée jusqu'à ce qu'une erreur d'insuffisance de mémoire se produise.
À partir de documentation , le retour de signature de Runtime.getRuntime().freeMemory()
est en tant que tel:
Retourne: une approximation de la quantité totale de mémoire actuellement disponible pour futurs objets alloués, mesurés 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'insuffisance de mémoire se produise (la mémoire libre) est susceptible d'être donnée par:
long presumableFreeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory;
Avec allocatedMemory
étant donné par:
long allocatedMemory =
(Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory());
La clé ici est une discordance entre le concept de mémoire libre. Une chose est la mémoire que le système d'exploitation fournit à la machine virtuelle Java. Un autre est le nombre total d'octets comprenant les blocs de mémoire actuellement utilisés par la machine virtuelle Java.
Considérant que la mémoire donnée aux applications Java est gérée par blocs par la machine virtuelle Java, la quantité de mémoire disponible disponible pour le Java La machine virtuelle peut ne pas correspondre exactement à la mémoire disponible pour une application Java.
Plus précisément, Christian Fries indique l'utilisation des indicateurs -mx
Ou -Xmx
Pour définir la quantité de mémoire maximale 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 déclarant que Runtime.getRuntime().freeMemory()
renvoie en fait ce que l'on pourrait appeler de la mémoire libre présumable; même si une allocation de mémoire future ne dépasse pas la valeur renvoyée par cette fonction, si la machine virtuelle Java n'a pas encore reçu le bloc de mémoire effectivement attribué par le système hôte, un Java.lang.OutOfMemoryError
peut encore être produit.
En fin de compte, la méthode appropriée à utiliser dépendra plus ou moins des spécificités de votre application.
Je fournis un autre lien qui peut être utile. C'est une question posée par l'utilisateur Richard Dormand et répondue par stones333 à propos de la détermination de la taille de segment par défaut Java utilisée.
Remarque: jusqu'à présent, toutes les réponses, même celles acceptées, semblent répondre à la question en disant que Runtime.getRuntime().freeMemory()
vous donne la quantité de mémoire pouvant être allouée jusqu'à ce qu'une erreur d'insuffisance de mémoire se produise. Cependant: c'est faux.
La quantité approximative approximative qui peut être allouée jusqu'à ce qu'une erreur d'insuffisance de mémoire se produise, c'est-à-dire que la mémoire disponible est probable.
long presumableFreeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory;
où
long allocatedMemory = (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory());
Explication: Si vous lancez la machine virtuelle via un paramètre -mx (ou -Xmx), vous indiquez le montant maximal disponible pour la machine virtuelle. Runtime.getRuntime().maxMemory()
vous donnera ce montant. À partir de cette quantité de mémoire système, la machine JVM allouera de la mémoire en fragments, par exemple des blocs de 64 Mo. Au démarrage, la JVM n'allouera qu'un tel bloc du système et non le montant total. Runtime.getRuntime().totalMemory()
donne la mémoire totale allouée par le système, tandis que Runtime.getRuntime().freeMemory()
vous donne la mémoire disponible dans le total mémoire allouée.
Par conséquent:
long definitelyFreeMemory = Runtime.getRuntime().freeMemory();
est la mémoire disponible déjà réservée par la machine virtuelle Java, mais il ne s'agit probablement que d'une petite quantité. Et vous aurez probablement presumableFreeMemory
. Bien sûr, vous pouvez obtenir une exception de mémoire insuffisante même si vous avez essayé d'allouer un montant inférieur à presumableFreeMemory
. Cela peut se produire si la machine virtuelle Java n'obtient pas le prochain bloc de mémoire du système. Cependant, sur la plupart des systèmes, cela ne se produira jamais et le système commencera plutôt à permuter - une situation que vous aimez éviter. W.r.t. à la question initiale: si -mx est défini sur une valeur raisonnable, alors presumableFreeMemory
est un bon indicateur de la mémoire disponible.
En plus d'utiliser les méthodes d'exécution, vous pouvez obtenir des informations supplémentaires sur la mémoire à l'aide de
MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heap = memBean.getHeapMemoryUsage();
MemoryUsage nonheap = memBean.getNonHeapMemoryUsage();
Chaque MemoryUsage fournit les valeurs init, utilisées, validées et maximales. Cela peut être utile si vous créez un thread de moniteur de mémoire qui interroge la mémoire et la consigne, vous fournissant ainsi un historique de l'utilisation de la mémoire au fil du temps. Parfois, il est utile de voir l’utilisation de la mémoire au fil du temps menant aux erreurs.
Si vous voulez vraiment prendre cela à l'extrême, créez un thread de vidage de tas. Surveillez votre utilisation de la mémoire au fil du temps et lorsqu'elle dépasse certains seuils, procédez comme suit (cela fonctionne avec JBoss 5.0 - votre kilométrage peut 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 examiner ces fichiers de vidage de tas avec Eclipse analyseur de mémoire ou des outils similaires pour vérifier les fuites de mémoire, etc.
En plus de l’autre réponse, je voudrais noter que ce n’est pas forcément une bonne idée, car vous pourriez avoir un cache dans votre application qui utilise SoftReferences .
Un tel cache libérerait de la mémoire dès que la JVM atteindrait ses limites de mémoire. L'allocation de mémoire, même s'il n'y a pas assez de mémoire disponible, provoquerait d'abord la libération de la mémoire par les références logicielles, et la rendrait disponible pour l'allocation.
Pour obtenir la mémoire disponible dans tout le 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();
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 consisterait à trouver un moyen de regrouper et de mettre à l'échelle vos services Web afin qu'ils puissent accepter 150% de la charge nominale sans refuser de nouvelles connexions. Cela ressemble à un exercice de dimensionnement vous apporterait une meilleure solution qu'un piratage de code.
Runtime.getRuntime().freeMemory()
est un moyen d'obtenir mémoire disponible pour la machine virtuelle à ce moment-là pendant l'exécution. Est-ce un bon moyen (ou) non, cela dépend complètement de votre application.
long freeMemory = Runtime.getRuntime().freeMemory();