Comment libérer de la mémoire en Java?

146

Existe-t-il un moyen de libérer de la mémoire en Java, similaire à la free()fonction C ? Ou est-ce que définir l'objet sur null et s'appuyer sur GC est la seule option?

Félix
la source
151
Ok ... clarifions une chose. Ce n'est pas parce que vous pensez que quelque chose est une mauvaise pratique et non quelque chose à encourager à faire que cela mérite un vote défavorable. C'est une question claire et valide, demandant s'il existe un moyen de libérer de la mémoire en Java sans compter sur le garbage collection. Bien que cela puisse être découragé et généralement pas utile ou une bonne idée, vous ne pouvez pas savoir qu'il n'y a pas de scénarios où cela peut être nécessaire sans savoir ce que Felix sait. Felix n'a peut-être même pas l'intention de l'utiliser. Il voudra peut-être simplement savoir si c'est possible. Cela ne mérite en aucun cas un vote contre.
Daniel Bingham
7
Pour clarifier, cela s'adresse à quiconque a voté contre cette proposition - pas nécessairement aux commentaires précédents.
Daniel Bingham

Réponses:

96

Java utilise la mémoire gérée, donc la seule façon d'allouer de la mémoire est d'utiliser l' newopérateur, et la seule façon de désallouer de la mémoire est de s'appuyer sur le garbage collector.

Ce livre blanc sur la gestion de la mémoire (PDF) peut vous aider à expliquer ce qui se passe.

Vous pouvez également appeler System.gc()pour suggérer que le garbage collector s'exécute immédiatement. Cependant, c'est le Java Runtime qui prend la décision finale, pas votre code.

Selon la documentation Java ,

L'appel de la méthode gc suggère que la machine virtuelle Java déploie des efforts pour recycler les objets inutilisés afin de rendre la mémoire qu'ils occupent actuellement disponible pour une réutilisation rapide. Lorsque le contrôle revient de l'appel de méthode, la machine virtuelle Java a fait de son mieux pour récupérer de l'espace sur tous les objets ignorés.

Daniel Pryden
la source
5
Cela force l'exécution du garbage collector. Cela ne l'oblige pas à libérer de la mémoire cependant ...
Pablo Santa Cruz
13
Non Pablo, cela ne force pas le GC à fonctionner.
Jesper
1
Une personne très fiable m'a dit que tous les ramasse-miettes de HotSpotVM ignorent System.gc()totalement.
Esko
1
Sur winXp, java SE GC exécute tous les System.gc () ou presque tous, mais le document API ne le garantit pas.
teodozjan
2
@Pablo Santa Cruz Que voulez-vous dire, il ne libère pas de mémoire? Je viens de le tester sur mon programme qui semblait avoir une fuite et l'utilisation de la RAM semblait se stabiliser? Et Daniel vient de dire que cela ne fait que le suggérer alors comment se fait-il que le pourcentage de bélier utilisé se stabilise toujours à chaque fois que j'appelle la méthode. Vous me confondez.
65

Personne ne semble avoir mentionné explicitement la définition de références aux objets null, ce qui est une technique légitime pour «libérer» la mémoire que vous voudrez peut-être envisager.

Par exemple, supposons que vous ayez déclaré un List<String> au début d'une méthode dont la taille a augmenté pour devenir très grande, mais qui n'a été nécessaire que jusqu'à la moitié de la méthode. Vous pouvez à ce stade définir la référence List sur nullpour permettre au garbage collector de récupérer potentiellement cet objet avant que la méthode ne se termine (et la référence tombe de toute façon hors de portée).

Notez que j'utilise rarement cette technique en réalité, mais cela vaut la peine d'être pris en compte lorsque vous traitez de très grandes structures de données.

Adamski
la source
8
Si vous faites vraiment beaucoup de travail sur un objet qui n'est utilisé que pour une partie d'une méthode, je suggère soit; votre méthode est trop compilée, divisez la méthode en parties avant et après, ou utilisez un bloc pour la première moitié du code (ce dernier est plus utile pour les scripts de test)
Peter Lawrey
5
L'endroit où la définition d'une référence d'objet sur null est important est lorsqu'elle est référencée à partir d'un autre objet de longue durée (ou éventuellement d'une variable statique). Par exemple, si vous avez un tableau d'objets volumineux de longue durée et que vous cessez d'utiliser l'un de ces objets, vous devez définir la référence du tableau sur null pour rendre l'objet disponible pour GC.
Hot Licks
22
System.gc(); 

Exécute le garbage collector.

L'appel de la méthode gc suggère que la machine virtuelle Java déploie des efforts pour recycler les objets inutilisés afin de rendre la mémoire qu'ils occupent actuellement disponible pour une réutilisation rapide. Lorsque le contrôle revient de l'appel de méthode, la machine virtuelle Java a fait de son mieux pour récupérer de l'espace sur tous les objets ignorés.

Non recommandé.

Edit: J'ai écrit la réponse originale en 2009. Nous sommes maintenant en 2015.

Les éboueurs se sont régulièrement améliorés au cours des ~ 20 ans d'existence de Java. À ce stade, si vous appelez manuellement le garbage collector, vous pouvez envisager d'autres approches:

  • Si vous forcez GC sur un nombre limité de machines, cela peut valoir la peine d'avoir un point d'équilibrage de charge loin de la machine actuelle, d'attendre qu'il finisse de servir aux clients connectés, d'expirer après un certain temps pour suspendre les connexions, puis juste dur -Redémarrez la JVM. C'est une solution terrible, mais si vous regardez System.gc (), les redémarrages forcés peuvent être un éventuel palliatif.
  • Pensez à utiliser un autre garbage collector. Par exemple, le collecteur G1 (nouveau au cours des six dernières années) est un modèle à faible pause; il utilise globalement plus de CPU, mais il est préférable de ne jamais forcer un arrêt brutal à l'exécution. Étant donné que les processeurs de serveur ont maintenant presque tous plusieurs cœurs, c'est un très bon compromis à avoir.
  • Regardez vos indicateurs de réglage de l'utilisation de la mémoire. Surtout dans les nouvelles versions de Java, si vous n'avez pas autant d'objets en cours d'exécution à long terme, envisagez d'augmenter la taille de newgen dans le tas. newgen (young) est l'endroit où les nouveaux objets sont alloués. Pour un serveur Web, tout ce qui est créé pour une requête est mis ici, et si cet espace est trop petit, Java passera plus de temps à mettre à niveau les objets vers une mémoire à plus longue durée de vie, où ils sont plus chers à tuer. (Si newgen est légèrement trop petit, vous allez le payer.) Par exemple, dans G1:
    • XX: G1NewSizePercent (la valeur par défaut est 5; cela n'a probablement pas d'importance.)
    • XX: G1MaxNewSizePercent (par défaut à 60; augmentez probablement ceci.)
  • Pensez à dire au ramasse-miettes que vous n'êtes pas d'accord avec une pause plus longue. Cela entraînera des exécutions de GC plus fréquentes, pour permettre au système de conserver le reste de ses contraintes. Dans G1:
    • XX: MaxGCPauseMillis (par défaut à 200.)
Dean J
la source
1
Commentant mon propre message, cela ne fait souvent rien, et l'appeler à plusieurs reprises peut rendre la JVM instable et ainsi de suite. Il peut également écraser votre chien; approche avec prudence.
Dean J
1
Je mettrais fortement l'accent sur la partie «suggère» de «L'appel de la méthode gc suggère que la JVM élargisse l'effort»
matt b
2
@Jesper, la réponse de Dean dit "suggère". En fait, il a posté la documentation exacte des javadocs de la méthode ...
matt b
2
@Software Monkey: Oui, j'aurais pu le modifier. Mais comme Dean J était manifestement actif (postant il y a seulement quelques minutes), j'ai pensé que c'était une courtoisie de lui demander de le faire. S'il ne l'avait pas fait, je serais revenu ici et j'aurais fait la modification et supprimé mon commentaire.
Daniel Pryden
1
Cela vaut également la peine de dire POURQUOI ce n'est pas recommandé. Si la JVM prête attention à la "suggestion" d'exécuter le GC, cela ralentira certainement l'exécution de votre application, peut-être de plusieurs ordres de grandeur!
Stephen C
11

* "Personnellement, je compte sur l'annulation des variables comme espace réservé pour une future suppression correcte. Par exemple, je prends le temps d'annuler tous les éléments d'un tableau avant de supprimer (rendre nul) le tableau lui-même."

Ceci est inutile. La façon dont le GC Java fonctionne est qu'il trouve des objets qui n'ont aucune référence à eux, donc si j'ai un Object x avec une référence (= variable) a qui pointe vers lui, le GC ne le supprimera pas, car il y a une référence à cet objet:

a -> x

Si vous null a que cela se produit:

a -> null
     x

Alors maintenant, x n'a pas de référence pointant vers lui et sera supprimé. La même chose se produit lorsque vous définissez a pour faire référence à un objet différent de x.

Donc, si vous avez un tableau arr qui fait référence aux objets x, y et z et une variable a qui fait référence au tableau, cela ressemble à ça:

a -> arr -> x
         -> y
         -> z

Si vous null a que cela se produit:

a -> null
     arr -> x
         -> y
         -> z

Ainsi, le GC trouve arr comme n'ayant aucune référence définie et le supprime, ce qui vous donne cette structure:

a -> null
     x
     y
     z

Maintenant, le GC trouve x, y et z et les supprime également. Annuler chaque référence dans le tableau ne fera rien de mieux, cela utilisera simplement du temps CPU et de l'espace dans le code (cela dit, cela ne fera pas mal plus que cela. Le GC sera toujours en mesure de fonctionner comme il le devrait. ).

Dakkaron
la source
5

Une raison valable de vouloir libérer de la mémoire de n'importe quel programme (java ou non) est de rendre plus de mémoire disponible pour d'autres programmes au niveau du système d'exploitation. Si mon application java utilise 250 Mo, je peux le forcer à 1 Mo et rendre le 249 Mo disponible pour d'autres applications.

Yios
la source
Si vous avez besoin de libérer explicitement un morceau de 249 Mo, dans un programme Java, la gestion de la mémoire ne serait pas la première chose sur laquelle je voudrais travailler.
Marc DiMillo
3
Mais la libération de stockage à l'intérieur de votre tas Java ne rend pas (dans le cas général) le stockage disponible pour d'autres applications.
Hot Licks
5

Pour prolonger la réponse et le commentaire de Yiannis Xanthopoulos et Hot Licks (désolé, je ne peux pas encore commenter!), Vous pouvez définir les options de la VM comme cet exemple:

-XX:+UseG1GC -XX:MinHeapFreeRatio=15 -XX:MaxHeapFreeRatio=30

Dans mon jdk 7, cela libèrera alors la mémoire de la VM inutilisée si plus de 30% du tas devient libre après GC lorsque la VM est inactive. Vous devrez probablement régler ces paramètres.

Bien que je ne l'ai pas vu souligné dans le lien ci-dessous, notez que certains garbage collector peuvent ne pas obéir à ces paramètres et que par défaut, java peut en choisir un pour vous, si vous avez plus d'un cœur (d'où l'argument UseG1GC ci-dessus ).

Arguments de VM

Mise à jour: Pour java 1.8.0_73, j'ai vu la JVM publier occasionnellement de petites quantités avec les paramètres par défaut. Semble ne le faire que si ~ 70% du tas est inutilisé cependant ... je ne sais pas si la libération serait plus agressive si le système d'exploitation manquait de mémoire physique.

nsandersen
la source
4

J'ai fait des expériences à ce sujet.

Il est vrai que cela System.gc();suggère uniquement d'exécuter le garbage collector.

Mais appeler System.gc();après avoir défini toutes les références à null, améliorera les performances et l'occupation de la mémoire.

Hemant Yadav
la source
Je pense que vous ne pouvez pas dire avec certitude que "appeler System.gc (); après avoir défini toutes les références à null, améliorera les performances et l'occupation de la mémoire.". Parce qu'il y a une énorme complexité de calcul de System.gc (). Et même après avoir appelé System.gc () et effectivement collecté les déchets, le jvm peut ne pas rendre la mémoire au système d'exploitation ou au système. JVM peut conserver la mémoire pour référence future. Voyez cette réponse .
Md. Abu Nafee Ibna Zahid
3

Si vous voulez vraiment allouer et libérer un bloc de mémoire, vous pouvez le faire avec des ByteBuffers directs. Il existe même un moyen non portable de libérer de la mémoire.

Cependant, comme cela a été suggéré, ce n'est pas parce que vous devez libérer de la mémoire en C que vous devez le faire.

Si vous pensez que vous avez vraiment un bon cas d'utilisation gratuit (), veuillez l'inclure dans la question afin que nous puissions voir ce que vous voulez faire, il est fort probable qu'il existe un meilleur moyen.

Peter Lawrey
la source
3

Entièrement à partir de javacoffeebreak.com/faq/faq0012.html

Un thread de faible priorité prend en charge automatiquement le garbage collection pour l'utilisateur. Pendant le temps d'inactivité, le thread peut être appelé, et il peut commencer à libérer de la mémoire précédemment allouée à un objet en Java. Mais ne vous inquiétez pas - cela ne supprimera pas vos objets sur vous!

Lorsqu'il n'y a aucune référence à un objet, cela devient un jeu équitable pour le ramasse-miettes. Plutôt que d'appeler une routine (comme free en C ++), vous affectez simplement toutes les références à l'objet à null, ou affectez une nouvelle classe à la référence.

Exemple :

public static void main(String args[])
{
  // Instantiate a large memory using class
  MyLargeMemoryUsingClass myClass = new MyLargeMemoryUsingClass(8192);

  // Do some work
  for ( .............. )
  {
      // Do some processing on myClass
  }

  // Clear reference to myClass
  myClass = null;

  // Continue processing, safe in the knowledge
  // that the garbage collector will reclaim myClass
}

Si votre code est sur le point de demander une grande quantité de mémoire, vous souhaiterez peut-être demander au garbage collector de commencer à récupérer de l'espace, plutôt que de lui permettre de le faire en tant que thread de faible priorité. Pour ce faire, ajoutez ce qui suit à votre code

System.gc();

Le garbage collector tentera de récupérer de l'espace libre et votre application pourra continuer à s'exécuter, avec autant de mémoire récupérée que possible (des problèmes de fragmentation de la mémoire peuvent s'appliquer sur certaines plates-formes).

Afficher un nom
la source
1

Dans mon cas, étant donné que mon code Java est destiné à être porté dans d'autres langages dans un proche avenir (principalement C ++), je veux au moins payer du bout des lèvres pour libérer correctement la mémoire afin que cela facilite le processus de portage plus tard.

Je compte personnellement sur l'annulation des variables comme espace réservé pour une future suppression appropriée. Par exemple, je prends le temps d'annuler tous les éléments d'un tableau avant de supprimer (rendre nul) le tableau lui-même.

Mais mon cas est très particulier, et je sais que je prends des performances en faisant cela.

Oskuro
la source
1

* "Par exemple, supposons que vous ayez déclaré une liste au début d'une méthode dont la taille est devenue très grande, mais qui n'a été requise que jusqu'à la moitié de la méthode. Vous pouvez à ce stade définir la référence de liste sur null pour permettre au garbage collector de récupérer potentiellement cet objet avant que la méthode ne se termine (et que la référence tombe de toute façon hors de portée). " *

C'est correct, mais cette solution peut ne pas être généralisable. Lors de la définition d'une référence d'objet List sur null - rendra la mémoire disponible pour le garbage collection, cela n'est vrai que pour un objet List de types primitifs. Si l'objet List contient à la place des types de référence, la définition de l'objet List = null ne déréférencera aucun des types de référence contenus dans la liste. Dans ce cas, la définition de l'objet List = null rendra orphelins les types de référence contenus dont les objets ne seront pas disponibles pour le garbage collection à moins que l'algorithme de garbage collection ne soit suffisamment intelligent pour déterminer que les objets sont orphelins.

Gothri
la source
1
Ce n'est en fait pas vrai. Le garbage collector Java est suffisamment intelligent pour gérer cela correctement. Si vous annulez la liste (et que les objets de la liste n'ont pas d'autres références à eux), le GC peut récupérer tous les objets de la liste. Il peut choisir de ne pas le faire pour le moment, mais il finira par les récupérer. Il en va de même pour les références cycliques. Fondamentalement, la façon dont le GC fonctionne est de rechercher explicitement des objets orphranés et de les récupérer. C'est tout le travail d'un GC. La façon dont vous le décrivez rendrait un GC totalement inutile.
Dakkaron
1

Althrough java fournit la collecte des ordures automatique parfois vous voulez savoir la taille de l'objet est et combien il est laissé mémoire à l' aide .Gratuit programatically import java.lang;et Runtime r=Runtime.getRuntime(); pour obtenir des valeurs de mémoire à l' aide mem1=r.freeMemory();d'appel de mémoire libre de la r.gc();méthode et l'appelfreeMemory()

Benjamin
la source
1

La recommandation de JAVA est d'assigner à null

Depuis https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html

L'affectation explicite d'une valeur nulle à des variables qui ne sont plus nécessaires aide le garbage collector à identifier les parties de mémoire qui peuvent être récupérées en toute sécurité. Bien que Java assure la gestion de la mémoire, il n'empêche pas les fuites de mémoire ou l'utilisation de quantités excessives de mémoire.

Une application peut provoquer des fuites de mémoire en ne libérant pas de références d'objet. Cela empêche le garbage collector Java de récupérer ces objets et entraîne une augmentation de la quantité de mémoire utilisée. L'annulation explicite des références aux variables après leur utilisation permet au garbage collector de récupérer de la mémoire.

Une façon de détecter les fuites de mémoire consiste à utiliser des outils de profilage et à prendre des instantanés de mémoire après chaque transaction. Une application sans fuite en état stable affichera une mémoire de tas active stable après les garbage collection.

user3869837
la source