Je travaille sur une application Java en ce moment et je travaille à optimiser son utilisation de la mémoire. Pour autant que je sache, je suis les directives pour un bon ramassage des ordures. Cependant, il semble que mon tas semble s'asseoir à sa taille maximale, même s'il n'est pas nécessaire.
Mon programme exécute une tâche gourmande en ressources une fois par heure, lorsque l'ordinateur n'est pas utilisé par une personne. Cette tâche utilise un morceau de mémoire décent, mais libère ensuite tout immédiatement après la fin de la tâche. Le profileur NetBeans révèle que l'utilisation de la mémoire ressemble à ceci:
J'aimerais vraiment rendre tout cet espace de tas au système d'exploitation lorsqu'il n'est pas utilisé. Il n'y a aucune raison pour moi de tout monopoliser alors que le programme ne fera même rien pendant au moins une autre heure.
Est-ce possible? Merci.
Vous pourriez peut-être jouer avec -XX:MaxHeapFreeRatio
- c'est le pourcentage maximum (par défaut 70) du tas qui est libre avant que le GC ne le rétrécisse. Peut-être que le régler un peu plus bas (40 ou 50?) Puis utiliser System.gc()
pourrait aller un peu plus loin pour vous obtenir le comportement souhaité?
Il n'y a aucun moyen de forcer cela à se produire, cependant, vous pouvez essayer d'encourager la JVM à le faire, mais vous ne pouvez pas simplement retirer la mémoire quand vous le souhaitez. Et bien que ce qui précède puisse réduire le tas, cette mémoire ne sera pas nécessairement restituée directement au système d'exploitation (bien que dans les implémentations récentes de la JVM, elle le fasse).
Version courte: Oui, vous le pouvez.
Version longue:
Pour la plupart des applications, les valeurs par défaut de la JVM sont correctes. Il semble que la JVM s'attende à ce que les applications ne s'exécutent que pendant une période limitée. Par conséquent, il ne semble pas libérer de mémoire seul.
Afin d'aider la JVM à décider comment et quand effectuer le ramasse-miettes, les paramètres suivants doivent être fournis:
-Xms
Spécifie la taille minimale du segment de mémoire–Xmx
Spécifie la taille maximale du tasPour les applications serveur, ajoutez: -server
Dans le cas où les paramètres mentionnés ci-dessus ne sont pas suffisants, vous pouvez influencer le comportement de la JVM concernant le garbage collection.
D'abord, vous pouvez utiliser System.gc()
pour indiquer à VM quand vous pensez que le garbage collection aurait du sens. Et deuxièmement, vous pouvez spécifier lequel des garbage collector la JVM devrait utiliser:
GC série
Paramètre de ligne de commande: -XX:+UseSerialGC
Arrête votre application et exécute GC.
GC parallèle
Paramètre de ligne de commande: -XX:+UseParallelGC -XX:ParallelGCThreads=value
Exécute collections mineures en parallèle avec votre application. Réduit le temps nécessaire pour collections majeures, mais utilise un autre thread.
GC de compactage parallèle
Paramètre de ligne de commande: -XX:+UseParallelOldGC
Exécute principales collections en parallèle avec votre application. Utilise plus de ressources CPU, réduit l'utilisation de la mémoire.
CMS GC
Paramètre de ligne de commande: -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=value -XX:+UseCMSInitiatingOccupancyOnly
Effectue des collections plus petites, et plus souvent que GC série, limitant ainsi les interruptions/arrêts de l'application.
G1
Paramètre de ligne de commande: -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC
Expérimental (au moins en Java 1.6): essaie de s'assurer que l'application n'est jamais arrêtée pendant plus d'une seconde.
Utilisation de la mémoire d'une application Web Play Framework sans aucune optimisation: Comme vous pouvez le voir, il utilise beaucoup d'espace de stockage et l'espace utilisé est régulièrement libéré.
Dans ce cas, les optimisations avec des paramètres uniquement n'étaient pas efficaces. Il y avait des tâches planifiées qui utilisaient plutôt beaucoup de mémoire. Dans ce cas, les meilleures performances ont été obtenues en utilisant le CMS GC
Combiné avec System.gc()
après les opérations gourmandes en mémoire. En conséquence, l'utilisation de la mémoire de la WebApp a été réduite de 1,8 Go à environ 400 à 500 Mo.
Vous pouvez voir ici une autre capture d'écran de VisualVM qui montre comment la mémoire est libérée par la JVM et effectivement retournée au système d'exploitation:
Remarque: J'ai utilisé le bouton "Perform GC" de VisualVM pour effectuer le GC plutôt que System.gc()
dans mon code, car les tâches planifiées qui consomment de la mémoire ne sont lancées qu'à des moments précis et quelque peu plus difficile à capturer avec VisualVM.
La JVM ne fonctionne pas de cette façon. Vous ne pouvez pas le rendre à l'OS.
Comme l'ont noté plusieurs personnes depuis qu'il a été écrit il y a quatre ans, vous pouvez redonner de la mémoire au système d'exploitation si vous donnez les paramètres GC appropriés à la JVM.
Java secret le mieux gardé: -Xincgc Cela affecte les performances mais pas toujours autant. Parfois, cela dépend de ce que vous faites. Le garbage collector incrémentiel restitue très bien la mémoire au système!
Une possibilité consiste à avoir votre arrière-plan Java lance une instance jvm externe chaque heure pour exécuter votre tâche. De cette façon, seule votre application jvm d'origine s'exécute entre les tâches.
Si votre application est au repos pendant les périodes d'inactivité, il est possible que le système d'exploitation permute ces pages pour vous, atténuant ainsi leur pression sur la mémoire physique.
http://www.linuxvox.com/2009/10/what-is-the-linux-kernel-parameter-vm-swappiness/
Java 12 prend en charge cette fonctionnalité à l'aide de G1GC.
JEP 346: renvoyer rapidement la mémoire validée non utilisée de G1.
Améliorez le garbage collector G1 pour retourner automatiquement Java mémoire de tas au système d'exploitation lorsqu'il est inactif.
https://openjdk.Java.net/jeps/346
Java 13 prend en charge cette fonctionnalité à l'aide de zgc
JEP 351: ZGC: désengager la mémoire inutilisée
ZGC ne désengage pas actuellement et ne renvoie pas de mémoire au système d'exploitation, même lorsque cette mémoire est inutilisée depuis longtemps. Ce comportement n'est pas optimal pour tous les types d'applications et d'environnements, en particulier ceux où l'empreinte mémoire est un problème. Par exemple: environnements de conteneurs où les ressources sont payées à l'utilisation.
Environnements dans lesquels une application peut rester inactive pendant de longues périodes et partage ou est en concurrence pour des ressources avec de nombreuses autres applications.
Une application peut avoir des exigences d'espace de tas très différentes lors de son exécution. Par exemple, le segment de mémoire nécessaire au démarrage peut être supérieur à ce qui est nécessaire ultérieurement lors de l'exécution en régime permanent.