web-dev-qa-db-fra.com

Existe-t-il un moyen de réduire Java tas lorsqu'il n'est pas utilisé?

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:

Java program memory usage

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.

51
jocull

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).

35
Michael Berry

Version courte: Oui, vous le pouvez.

Version longue:

Comment Java/JVM gère la mémoire

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 tas

Pour les applications serveur, ajoutez: -server

Ça ne me suffit pas. Je veux plus de contrôle!

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:

Différents types de collecteurs d'ordures:

  • 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.

    Exemple

Utilisation de la mémoire d'une application Web Play Framework sans aucune optimisation: Play Framework WebApp without optimizations 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:

Memory is being freed by the JVM and returned to the OS 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.

Lectures complémentaires

14
Peanut

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.

4
duffymo

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!

3
Mike

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.

3
robert_x44

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/

3
Ron

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.

http://openjdk.Java.net/jeps/351

2
AlexB