web-dev-qa-db-fra.com

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

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

133
Felix

Java utilise la mémoire gérée. Par conséquent, la seule façon d'allouer de la mémoire consiste à utiliser l'opérateur new. La seule façon de libérer de la mémoire consiste à faire appel au garbage collector.

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

Vous pouvez également appeler System.gc() pour suggérer que le ramasse-miettes s'exécute immédiatement. Cependant, le Java Runtime prend la décision finale et non votre code.

Selon le documentation Java ,

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

91
Daniel Pryden

Personne ne semble avoir mentionné explicitement la définition de références d'objet à null, qui est une technique légitime pour "libérer" de la mémoire que vous pouvez envisager.

Par exemple, supposons que vous ayez déclaré un List<String> au début d'une méthode dont la taille a grossi, mais qui n'a été nécessaire que jusqu'au milieu de la méthode. À ce stade, vous pouvez définir la référence de liste sur null pour autoriser le récupérateur de mémoire à récupérer potentiellement cet objet avant la fin de la méthode (et la référence tombe de toute façon dans l'étendue).

Notez que j’utilise rarement cette technique dans la réalité, mais cela vaut la peine d’être pris en compte lorsqu’il s’agit de très grandes structures de données.

63
Adamski
System.gc(); 

Lance le ramasse-miettes.

Appel de la méthode gc suggère que la Java machine virtuelle s’efforce de 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 est renvoyé à partir de l'appel de la méthode, la machine virtuelle Java a fait de son mieux pour récupérer l'espace de 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 progressivement améliorés depuis environ 20 ans que Java existe. À ce stade, si vous appelez manuellement le ramasse-miettes, vous pouvez envisager d'autres approches:

  • Si vous forcez le CPG sur un nombre limité de machines, il peut être intéressant d’avoir un point d’équilibrage de charge à l’écart de la machine en cours, en attente de finissez de servir aux clients connectés, arrêtez le délai après un certain temps pour suspendre les connexions, puis redémarrez complètement la JVM. C'est une solution épouvantable, mais si vous examinez System.gc (), un redémarrage forcé peut constituer un palliatif.
  • Pensez à utiliser un autre ramasse-miettes. Par exemple, le collecteur G1 (nouveau dans les six dernières années) est un modèle à faible pause; Dans l’ensemble, il utilise plus de ressources processeur, mais fait de son mieux pour ne jamais forcer un arrêt définitif lors de l’exécution. Étant donné que les processeurs de serveur ont maintenant presque tous plusieurs cœurs, il s'agit d'un très bon compromis.
  • Regardez vos drapeaux en réglant l'utilisation de la mémoire. Surtout dans les nouvelles versions de Java, si vous n'avez pas beaucoup d'objets en cours d'exécution à long terme, pensez à 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 placé ici, et si cet espace est trop petit, Java passera plus de temps à mettre à niveau les objets vers une mémoire plus longue, où ils coûtent plus cher à tuer. (Si newgen est un peu trop petit, vous allez payer pour cela.) Par exemple, dans G1:
    • XX: G1NewSizePercent (la valeur par défaut est 5; cela n'a probablement pas d'importance.)
    • XX: G1MaxNewSizePercent (la valeur par défaut est 60; augmentez probablement cette valeur.)
  • Pensez à dire au ramasse-miettes que vous ne voulez pas une pause plus longue. Cela entraînera des exécutions de GC plus fréquentes, afin de permettre au système de conserver le reste de ses contraintes. En G1:
    • XX: MaxGCPauseMillis (la valeur par défaut est 200.)
22
Dean J

* "Je m'appuie personnellement sur l'annulation de variables en tant qu'espace réservé pour une suppression ultérieure appropriée. Par exemple, je prends le temps d'annuler tous les éléments d'un tableau avant de supprimer (en rendant NULL) le tableau lui-même."

C'est inutile. La manière dont fonctionne Java GC consiste à trouver des objets qui n'y sont référencés. Par conséquent, si j'ai un objet x avec une référence (= variable) a qui le désigne, le GC ne le supprimera pas. , car il y a une référence à cet objet:

a -> x

Si vous avez une valeur nulle, cela se produit:

a -> null
     x

Alors maintenant, x n'a pas de référence pointant vers elle et sera supprimé. La même chose se produit lorsque vous définissez une 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, il ressemble à ceci:

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

Si vous avez une valeur nulle, cela se produit:

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

Donc, le GC trouve arr comme n'ayant pas de référence 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. La suppression de chaque référence dans la matrice ne fera rien de mieux, elle utilisera simplement du temps processeur et de la place dans le code (cela dit, cela ne fera pas mal plus longtemps. Le GC pourra toujours fonctionner comme il se doit. ).

11
Dakkaron

Une raison valable pour vouloir libérer de la mémoire de tout programme (Java ou non) est de rendre plus de mémoire disponible pour les autres programmes au niveau du système d'exploitation. Si mon application Java utilise 250 Mo, je souhaiterais peut-être la réduire à 1 Mo et rendre les 249 Mo disponibles pour d'autres applications.

5
Yios

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

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

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

Bien que le lien ci-dessous ne le souligne pas, notez que certains éboueurs peuvent ne pas respecter ces paramètres et que, par défaut, Java peut en choisir un pour vous, si vous aviez plusieurs cœurs (d'où l'argument UseG1GC ci-dessus).

arguments de la VM

Mise à jour: pour Java 1.8.0_73, j'ai vu la machine virtuelle Java publier occasionnellement de petites quantités avec les paramètres par défaut. Ne semble le faire que si environ 70% du segment de mémoire est inutilisé. Je ne sais pas si le relâchement serait plus agressif si le système d'exploitation n'avait pas beaucoup de mémoire physique.

5
nsandersen

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

Il est vrai que System.gc(); ne suggère que l'exécution du ramasse-miettes.

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

4
Hemant Yadav

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é, le simple fait de libérer de la mémoire en C ne signifie pas que ce soit une bonne idée de le faire.

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

3
Peter Lawrey

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

Un thread de faible priorité prend en charge le nettoyage de la mémoire automatiquement pour l'utilisateur. Pendant l'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 vous 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 pouvez demander au récupérateur de mémoire de commencer à récupérer de l'espace, plutôt que de lui permettre de le faire en tant que thread à faible priorité. Pour ce faire, ajoutez ce qui suit à votre code

System.gc();

Le ramasse-miettes va tenter de récupérer de l'espace libre, et votre application peut continuer à s'exécuter avec autant de mémoire que possible (des problèmes de fragmentation de la mémoire peuvent survenir sur certaines plates-formes).

2
displayname

* "Par exemple, supposons que vous ayez déclaré une liste au début d’une méthode dont la taille a été très grande, mais n’a été nécessaire que jusqu’à mi-parcours de la méthode. Vous pouvez maintenant définir le paramètre Indiquez la référence à null pour autoriser le ramasse-miettes à potentiellement récupérer cet objet avant la fin de la méthode (et la référence tombe de toute façon dans la portée). "*

Ceci 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 -will- libérer de la mémoire pour le nettoyage de la mémoire, cela n'est vrai que pour un objet List de types primitifs. Si l'objet List contient plutôt des types de référence, définir l'objet List = null ne déréférencera aucune des types de référence contenus dans la liste. Dans ce cas, définir l'objet List = null pour orphelin 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 ont été orphelins.

1
Gothri

Dans mon cas, comme mon code Java est destiné à être porté dans d'autres langues dans un avenir proche (principalement C++), je souhaite au moins faire de mon mieux pour libérer de la mémoire, ce qui facilitera le processus de portage ultérieurement. sur.

Je m'appuie personnellement sur l'annulation de variables comme espace réservé pour une suppression ultérieure 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 cela me frappe beaucoup.

1
Oskuro

Bien que Java fournisse automatiquement une récupération de place, vous voudrez peut-être connaître la taille de l'objet et combien il en reste. Libérez de la mémoire à l'aide de la méthode import Java.lang; et Runtime r=Runtime.getRuntime(); pour obtenir les valeurs de en utilisant mem1=r.freeMemory(); pour libérer de la mémoire, appelez la méthode r.gc(); et l'appel freeMemory()

1
Benjamin

La recommandation de Java est d'assigner à null

De https://docs.Oracle.com/cd/E19159-01/819-3681/abebi/index.html

L'affectation explicite d'une valeur null à des variables dont vous n'avez plus besoin aide le récupérateur de mémoire à identifier les parties de la mémoire pouvant ê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 ni l'utilisation d'une quantité excessive de mémoire.

Une application peut induire des fuites de mémoire en ne libérant pas les références d'objet. Cela empêche le récupérateur de déchets Java de récupérer ces objets, ce qui entraîne une augmentation de la quantité de mémoire utilisée. Si vous annulez explicitement les références aux variables après leur utilisation, le ramasse-miettes peut 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 à l'état stable affichera une mémoire de tas active constante après les collectes de mémoire.

1
user3869837