Java Language Collecte des ordures


Exemple

L'approche C ++ - new and delete

Dans un langage comme le C ++, le programme d'application est responsable de la gestion de la mémoire utilisée par la mémoire allouée dynamiquement. Lorsqu'un objet est créé dans le tas C ++ à l'aide de l'opérateur new , il doit y avoir une utilisation correspondante de l'opérateur delete pour éliminer l'objet:

  • Si le programme oublie de delete un objet et ne fait que l'oublier, la mémoire associée est perdue pour l'application. Le terme pour cette situation est une fuite de mémoire , et trop de mémoire fuit, une application est susceptible d’utiliser de plus en plus de mémoire et finit par tomber en panne.

  • D'un autre côté, si une application tente de delete le même objet deux fois ou d'utiliser un objet après sa suppression, l'application risque de se bloquer en raison de problèmes de corruption de mémoire.

Dans un programme C ++ compliqué, l'implémentation de la gestion de la mémoire en utilisant new et delete peut prendre beaucoup de temps. En effet, la gestion de la mémoire est une source commune de bogues.

L'approche Java - garbage collection

Java adopte une approche différente. Au lieu d'un opérateur de delete explicite, Java fournit un mécanisme automatique appelé récupération de place pour récupérer la mémoire utilisée par les objets devenus inutiles. Le système d'exécution Java prend la responsabilité de trouver les objets à éliminer. Cette tâche est effectuée par un composant appelé « garbage collector» ou «GC».

A tout moment pendant l'exécution d'un programme Java, nous pouvons diviser l'ensemble de tous les objets existants en deux sous-ensembles distincts 1 :

  • Les objets accessibles sont définis par le JLS comme suit:

    Un objet accessible est tout objet auquel on peut accéder dans tout calcul continu potentiel à partir d'un thread en direct.

    En pratique, cela signifie qu’il existe une chaîne de références à partir d’une variable locale dans la portée ou d’une variable static permettant à un code d’atteindre l’objet.

  • Les objets inaccessibles sont des objets qui ne peuvent pas être atteints comme ci-dessus.

Tout objet inaccessible est éligible pour la récupération de la mémoire. Cela ne signifie pas qu'ils seront collectés. En réalité:

  • Un objet inaccessible n'est pas collecté immédiatement après être devenu inaccessible 1 .
  • Un objet inaccessible peut ne jamais être récupéré.

La spécification du langage Java donne beaucoup de latitude à une implémentation JVM pour décider quand collecter des objets inaccessibles. Il donne également (en pratique) l'autorisation à une implémentation JVM d'être prudente dans la manière dont elle détecte les objets inaccessibles.

La seule chose que JLS garantit, c'est qu'aucun objet accessible ne sera jamais récupéré.

Que se passe-t-il lorsqu'un objet devient inaccessible

Tout d'abord, rien ne se produit spécifiquement lorsqu'un objet devient inaccessible. Les choses ne se produisent que lorsque le ramasse-miettes s'exécute et qu'il détecte que l'objet est inaccessible. De plus, il est fréquent qu'un cycle de CPG ne détecte pas tous les objets inaccessibles.

Lorsque le CPG détecte un objet inaccessible, les événements suivants peuvent se produire.

  1. S'il existe des objets Reference faisant référence à l'objet, ces références seront effacées avant la suppression de l'objet.

  2. Si l'objet est définissable , il sera finalisé. Cela se produit avant que l'objet soit supprimé.

  3. L'objet peut être supprimé et la mémoire qu'il occupe peut être récupérée.

Notez qu'il existe une séquence claire dans laquelle les événements ci-dessus peuvent se produire, mais rien n'oblige le ramasse-miettes à effectuer la suppression finale d'un objet spécifique dans un délai spécifique.

Exemples d'objets accessibles et inaccessibles

Prenons les exemples de classes suivants:

// A node in simple "open" linked-list.
public class Node {
    private static int counter = 0;

    public int nodeNumber = ++counter;
    public Node next;
}

public class ListTest {
    public static void main(String[] args) {
        test();                    // M1
        System.out.prinln("Done"); // M2
    }
    
    private static void test() {
        Node n1 = new Node();      // T1
        Node n2 = new Node();      // T2
        Node n3 = new Node();      // T3
        n1.next = n2;              // T4
        n2 = null;                 // T5
        n3 = null;                 // T6
    }
}

Examinons ce qui se passe quand on appelle test() . Les instructions T1, T2 et T3 créent des objets Node , et tous les objets sont accessibles via les variables n1 , n2 et n3 , respectivement. L'instruction T4 assigne la référence à l'objet 2nd Node au champ next du premier. Lorsque cela est fait, le 2ème Node est accessible via deux chemins:

 n2 -> Node2
 n1 -> Node1, Node1.next -> Node2

Dans l'instruction T5, nous affectons null à n2 . Cela brise la première des chaînes d'accessibilité pour Node2 , mais la seconde reste intacte, donc Node2 est toujours accessible.

Dans l'instruction T6, nous affectons null à n3 . Cela casse la seule chaîne d'accessibilité pour Node3 , ce qui rend inaccessible Node3 . Cependant, Node1 et Node2 sont tous deux encore accessibles via la n1 variable.

Enfin, lorsque la méthode test() retourne, ses variables locales n1 , n2 et n3 sont hors de portée et ne peuvent donc pas être consultées. Cela brise les chaînes restantes pour joignabilité Node1 et Node2 , et tous les Node objets sont ni inaccessibles et admissibles à la collecte des ordures.


1 - Ceci est une simplification qui ignore la finalisation et les classes de Reference . 2 - Hypothétiquement, une implémentation Java pourrait le faire, mais le coût de la performance le rend peu pratique.