IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Java Discussion :

comment tracer un Objet dont le GC ne veut pas?


Sujet :

Langage Java

  1. #1
    Membre Expert
    Avatar de professeur shadoko
    Homme Profil pro
    retraité nostalgique Java SE
    Inscrit en
    Juillet 2006
    Messages
    1 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 77
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : retraité nostalgique Java SE

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 257
    Par défaut comment tracer un Objet dont le GC ne veut pas?
    voilà: J'écris une classe ( faisant appel à d'autres codes) et mon intuition me fait soupçonner que je vais avoir des problèmes avec les objets de ce type.
    J'écris donc des tests dont un qui consiste à rajouter un finalize pour tracer la récupération de l'objet, créer un tel objet, mettre la référence à null puis en boucle créer un paquet d'objets qui sature la mémoire.
    D'habitude on voit la trace ... mais pas là! Donc je me torture l'esprit pour essayer de comprendre où cet objet est encore référencé ... et mon neurone refuse de me trouver une solution
    J'essaye de reproduire le problème avec des objets analogues ... qui eux n'ont pas de problème! A force de supprimer des références membres j'ai trouvé la référence membre qui déclenche le souci: sauf que je ne comprends pas en quoi cette référence qui n'a pas elle même de référence sur l'objet "contenant" peut poser problème....
    Le code a à peu près cette allure:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    class ClasseAProbleme {
            // quelques variables membres
            private TypeA refA ; // TypeA n'est pas une classe membre d'instance
            private TypeB refB ; // même remarque
         public ClassAProbleme() {
              // initialisations diverses
              refa = new TypeA(this.var1) ;
              refB = new TypeB(refa) ;
         }
         // si je met refB à null ça marche .....  
    }
     
    // code du main dans un test
          ClasseAProbleme probleme = new ClasseAProbleme() ;
          probleme = null ;
          // boucle créant des tonnes d'objets
          // pas de trace si refB est initialisée
          // trace si refB à null
    Bien entendu ce code ne donne pas d'indications sur le fond du problème qui est sans doute lié à la nature de "TypeB"....
    Donc question: quelqu'un pourrait-il m'indiquer un moyen de "tracer" les références liées à cet objet au travers d'un debuggueur rusé?
    Merci

  2. #2
    Membre Expert
    Avatar de professeur shadoko
    Homme Profil pro
    retraité nostalgique Java SE
    Inscrit en
    Juillet 2006
    Messages
    1 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 77
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : retraité nostalgique Java SE

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 257
    Par défaut
    bon après avoir fait des tonnes de tests sur des codes équivalents je suis arrivé à une conclusion: bug du garbage collector ....
    j'aime pas trop ce genre de conclusion car en général c'est nous qui faisons des bugs... je soumets encore mes codes à des spécialistes mais j'ai peu d'espoir d'avoir moi même commis un bug
    pour revenir à la question initiale: un moyen de tracer l'opinion du GC????

  3. #3
    Membre Expert Avatar de Uther
    Homme Profil pro
    Tourneur Fraiseur
    Inscrit en
    Avril 2002
    Messages
    4 746
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Tourneur Fraiseur

    Informations forums :
    Inscription : Avril 2002
    Messages : 4 746
    Par défaut
    Est-ce que tu pourrais nous fournir un code minimum et compilable qui reproduise ton problème?

  4. #4
    Membre Expert
    Avatar de professeur shadoko
    Homme Profil pro
    retraité nostalgique Java SE
    Inscrit en
    Juillet 2006
    Messages
    1 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 77
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : retraité nostalgique Java SE

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 257
    Par défaut
    voila un exemple analogue (car mon code est trop complexe)
    dans lequel le premier objet n'est jamais récupéré
    (désolé code un peu chargé)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
     
    package garbage;
     
    import java.io.Closeable;
    import java.util.ArrayList;
     
    /**
     * This code to try to understand a GC strategy.
     * IMHO the first object in the main should be reclaimed: it is not!
     * why?
     * I suspect a bug in the mark and sweep due to cycles ....
     */
    public class TestReachable {
    	/*
    	 * these object are not immmediately garbaged since they are referenced
    	 * from a static ArrayList
    	 */
    	static class Closer implements Closeable {
    		Object thing ;
    		Closer(Object thing ){
    			this.thing = thing ;
    		}
     
    		public void close() {
    			System.out.println("CLOSING");
    		}
     
    	}
     
     
    	static class SafeCloser {
    		static ArrayList<Closer> closerList = new ArrayList<Closer>() ;
     
    		Closer closer ;
    		SafeCloser(Closer clos) {
    			this.closer = clos ;
    			closerList.add(clos) ;
    		}
    		public void finalize() {
    			System.out.println("FINALIZE safeCloser");
    			closerList.remove(closer) ;
    			closer.close() ;
    		}
    	}
     
    	static class ReclaimableObject {
    		public void finalize() {
    			System.out.println("FINALIZE A " 
    				+ name + " ;thing : " + thing +" ; safe: " + safe) ;
    		}
    		Object thing ;
    		Closer closer ;
    		SafeCloser safe ;
    		String name ;
     
    		ReclaimableObject (String name ,boolean withObject, boolean withSafe) {
    			this.name = name ;
    			if(withObject) {
    				this.thing = new Object() ;
    			}
    			this.closer = new Closer(thing) ;
    			if(withSafe) {
    				this.safe = new SafeCloser(closer) ;
    			}
    		}
     
    	}
     
    	public static void main(String[] args) throws Exception {
     
    		// this object will NOT be reclaimed
    		ReclaimableObject object = new ReclaimableObject("ONE" ,true, true) ;
    		object = null ;
    		// this object will be reclaimed
    		ReclaimableObject object2 = new ReclaimableObject("TWO" ,false, true) ;
    		object2 = null ; // this object will be reclaimed
    		ReclaimableObject object3 = new ReclaimableObject("THREE", true, false) ;
    		object3 = null ;
    		for(int ix = 0 ; ix < 10000 ; ix++) {
    			long[] array = new long[1000] ;
    		}
    		Thread.sleep(3000L );
    		System.out.println("MID WAY");
    		// this object WILL  be reclaimed !!!!! (go figure!)
    		object= new ReclaimableObject("ONE-2" ,true, true) ;
    		object= null ;
    		// this object will be reclaimed
    		object2 = new ReclaimableObject("TWO-2" ,false, true) ;
    		object2 = null ;
    		// this object will be reclaimed
    		object3 = new ReclaimableObject("THREE-2", true, false) ;
    		object3 = null ;
    		for(int ix = 0 ; ix < 10000 ; ix++) {
    			long[] array = new long[1000] ;
    		}
    		Thread.sleep(3000L );
    		System.out.println("THE END");
    	}
     
     
    }
    bien sur ce que je cherche c'est un outil de trace du GC...pour comprendre

  5. #5
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 483
    Par défaut
    Je n'arrive pas a reproduire ton problème. Mon output est le suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    FINALIZE A THREE ;thing : java.lang.Object@1feed786 ; safe: null
    FINALIZE safeCloser
    CLOSING
    FINALIZE A TWO ;thing : null ; safe: garbage.TestReachable$SafeCloser@7987aeca
    FINALIZE safeCloser
    CLOSING
    FINALIZE A ONE ;thing : java.lang.Object@3ae48e1b ; safe: garbage.TestReachable$SafeCloser@732dacd1
    MID WAY
    FINALIZE A THREE-2 ;thing : java.lang.Object@5d0385c1 ; safe: null
    FINALIZE safeCloser
    CLOSING
    FINALIZE A TWO-2 ;thing : null ; safe: garbage.TestReachable$SafeCloser@1242719c
    FINALIZE safeCloser
    CLOSING
    FINALIZE A ONE-2 ;thing : java.lang.Object@4830c221 ; safe: garbage.TestReachable$SafeCloser@7919298d
    THE END
    De plus ton code garanti bien que tes objets seront garbage collectables, pas qu'ils seront collectés. Le GC n'intervendra que si il est à cours de mémoire. Un test "correct" pour démontrer qu'un code ne fonctionne pas correctement serait plutot celui-ci


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    while(true){
    		ReclaimableObject object = new ReclaimableObject("ONE" ,true, true) ;
    		object = null ;
    		// this object will be reclaimed
    		ReclaimableObject object2 = new ReclaimableObject("TWO" ,false, true) ;
    		object2 = null ; // this object will be reclaimed
    		ReclaimableObject object3 = new ReclaimableObject("THREE", true, false) ;
    		object3 = null ;
    }
    // si un objet n'est pas GCisable, on finira avec un OutOfMemoryError, sinon on est en boucle infinie. Hint: gonfler 'reclaimableObject' en lui mettant un champ de 1 ou 2 M pour accélérer l'éventuelle OutOfMemory

  6. #6
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Salut,


    Ce n'est pas un bug en tant que tel, mais plutôt un "effet de bord" de l'utilisation d'un GC.

    Tu ne peux pas prévoir le moment où tes objets seront collectés, ni dans quel ordre ils le seront. Le GC peut très bien conservé en mémoire un objet très "longtemps" après qu'il ne soit plus accessible.


    Perso j'arrive à reproduire ton "cas" sous Windows, mais uniquement avec la JVM client (la JVM server doit utilise un algo différent pour le GC).



    En exécutant avec l'option -verbose:gc pour surveiller le GC on a plus d'info.
    Avec la JVM cliente on se retrouve avec plusieurs lignes comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    [GC 1020K->132K(5056K), 0.0001950 secs]
    Cela correspond à une libération de mémoire par le GC. Principalement dû à tes boucles qui crées des tableaux. Comme indiqué la mémoire utilisée passe de 1020K à 132K sans jamais dépassé la quantité initial de 5056K).

    Or il s'agit de GC "simple", qui se contente de libérer ce qu'il est possible de libérer rapidement. Et pour une raison que je ne connais pas en détail, ton premier objet ne semble pas rentrer dans ce cas, et il n'est donc jamais libéré. C'est justement là tout son intérêt, car cela évites de faire des traitements trop lourd dans le GC inutilement (on conserve de 80% de mémoire libre dans le heap - inutile de surcharger le GC pour rien).



    Par contre si tu utilises plus de mémoire, le GC sera obligé de faire un fullGC plus "couteux" en temps, et là ton objet sera bel et bien supprimé.

    Tu peux vérifier cela en instanciant un objet de taille plus grande que ton heap initial :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    byte[] bytes = new byte[8000000]; // Taille à adapter selon le heap initial
    Cela va forcément généré un fullGC qui va libérer ton objet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    [Full GC 132K->132K(5056K), 0.0134333 secs]
    FINALIZE safeCloser
    CLOSING
    FINALIZE A TWO ;thing : null ; safe: TestReachable$SafeCloser@fa9cf
    FINALIZE A ONE ;thing : null ; safe: null
    THE END



    Il n'y a pas de bug ni de comportement anormal : c'est le fonctionnement même du GC.


    Par contre on dirait que tu veux utiliser la finalisation pour mettre en place un mécanisme de libération des ressources, et ça c'est une grave erreur !

    On ne peut pas se baser sur la finalisation !


    a++

  7. #7
    Membre Expert
    Avatar de professeur shadoko
    Homme Profil pro
    retraité nostalgique Java SE
    Inscrit en
    Juillet 2006
    Messages
    1 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 77
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : retraité nostalgique Java SE

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 257
    Par défaut
    bien vu!

    Citation Envoyé par adiGuba Voir le message
    Par contre on dirait que tu veux utiliser la finalisation pour mettre en place un mécanisme de libération des ressources, et ça c'est une grave erreur !

    On ne peut pas se baser sur la finalisation !
    tout à fait! mais comme je l'ai dit ce n'est pas le code initial!
    (pour préciser un peu: c'est un mécanisme mixte avec finalize et shutdownhook qui garantit une fermeture, de plus il y a un timeout)

  8. #8
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    En fait il ne faut surtout pas utiliser finalize pour cela, car justement tu ne peux pas prédire le moment où la ressource sera libéré. Pire : elle pourrait très bien ne jamais être libéré (à l'arrêt du programme, les finalisations en attente ne sont pas exécuté).


    Au mieux le finalize() pourrait être un garde-fou en cas d'oubli... mais il est préférable de toujours libérer les ressources explicitement via un appel de la méthode close() (ou équivalente), le tout via un try/finally propre afin de s'assurer de son exécution dans tous les cas.

    La gestion des ressources est hors du contrôle du GC !

    a++

  9. #9
    Membre Expert
    Avatar de professeur shadoko
    Homme Profil pro
    retraité nostalgique Java SE
    Inscrit en
    Juillet 2006
    Messages
    1 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 77
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : retraité nostalgique Java SE

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 257
    Par défaut
    bon je me suis mal expliqué. donc un peu plus de détails
    j'ai une liste d'action dont le déclenchement dans le temps ne pose pas problème mais doit être (raisonnablement) garanti.
    le code peut fermer explicitement l'objet en charge de vidanger ces actions (d'ou le terme "close")
    sinon :
    - déclenchement à l'arrêt de la machine virtuelle (shutdownhook)
    - déclenchement sur time out
    - déclenchement sur finalize

    tu remarqueras que ce dernier est le moins important mais est nécessaire car ces objets peuvent être évacués de la mémoire sans un "close" explicite.

    c'est en vérifiant tous les cas possibles (je suis assez parano) que j'ai cru tomber sur un problème (apparemment je suis pas assez systématique dans ma paranoia )

    edit: oui finally pas possible car hors de portée syntaxique dans la majorité des cas.

    re edit: dans mon vrai code c'est pas finalize qui fait le close! c'est un autre code

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 11/03/2015, 20h33
  2. Réponses: 5
    Dernier message: 23/02/2006, 15h18
  3. Comment afficher une form dont les dimensions ne dépendent pas de la résolution sous
    Par The Freestyler Fou dans le forum Vos contributions VB6
    Réponses: 1
    Dernier message: 10/09/2005, 16h05
  4. Réponses: 6
    Dernier message: 30/06/2004, 09h16
  5. Comment mettre plusieurs objets ds un composant ?
    Par Fleury dans le forum Composants VCL
    Réponses: 7
    Dernier message: 24/05/2003, 18h34

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo