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

JSF Java Discussion :

ExtendedDataModel et fuites de mémoire


Sujet :

JSF Java

  1. #1
    Membre habitué
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2008
    Messages
    179
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Octobre 2008
    Messages : 179
    Points : 172
    Points
    172
    Par défaut ExtendedDataModel et fuites de mémoire
    Bonjour,

    me voilà aujourd'hui avec un problème qui hante pas mal de dévelopeurs... le célèbre OutOfMemoryError : PermGen Space...
    Donc usage d'un profiler oblige, je traque mes objets, et découvre qu'un de mes objets (une classe hibernate pour être précis!) qui est utilisé pour remplir une Datatable se multiplie à chaque rechargement de la table... et ne désemplit jamais (même si je force le garbage collector à faire son boulot).

    J'ai dans mon appli deux tables du même genre remplies avec les mêmes objets :
    • La première utilise un ExtendedDataModel pour réaliser une "vraie" pagination
    • La Seconde est simplement liée à une "List" (sans dataModel).


    Après une série de tests, je constate que tout se passe normalement dans le second cas (les objets créés sont effacés par le ramasse miettes lorsque la table n'est plus affichée).
    Dans le premier cas, en revanche, (celui qui fait l'objet de ma question) mes objets se multiplient à chaque rechargement de la table...

    J'ai pris l'exemple fourni par richfaces (ici) qui est à peu de choses près identique à mon cas (à l'objet de ligne près en gros).

    Il y a beaucoup de choses qui gravitent ici (le modèle, la table, les technos, etc.), et je ne suis pas certain que tout coller ici simplifierait la compréhension. Dans le cas contraire, je fournirai volontiers le bout de code ou l'info qui manque.

    Pour résumer le problème : mes objets s'accumulent en mémoire à un ratio qui n'est même pas un multiple du nombre d'objets affichés (ou existant en base)... et ne sont pas nettoyés par le garbage collector!

    Bref... si vous avez des idées ou avez déjà expérimenté un problème similaire, je suis preneur pour toute piste!


    PS : il s'agit d'un problème lié à la session exclusivement puisque le fait de me déconnecter rend tous les objets concernés bons à ramasser par le GC.

  2. #2
    Membre averti Avatar de Shinzul
    Homme Profil pro
    Lecteur assidu de code source
    Inscrit en
    Janvier 2008
    Messages
    174
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Lecteur assidu de code source
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2008
    Messages : 174
    Points : 333
    Points
    333
    Par défaut
    Bon pour les pistes déjà si c'est pas supprimer c'est donc qu'il reste un lien quelque part entre tes objets qui ne les fais pas supprimer par le GC (Ouais la réponse pour dire ce que tout le monde pense \o/)

    Après je te dirai de faire une inspection de ton objets lors de la suppression de ta session pour voir si ta classe ne contiendrait trop d'éléments. Dans l'exemple de richfaces le scope n'est pas donné et je ne pense pas avoir vu de méthode de clean.
    Si le tiens est scopé session ca peux venir de là.

  3. #3
    Membre habitué
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2008
    Messages
    179
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Octobre 2008
    Messages : 179
    Points : 172
    Points
    172
    Par défaut
    Bonjour, et merci de ta réponse.

    Quelques précisions alors:
    * mon dataModel est inclus dans un bean de scope request (donc - a priori - pas de rapport avec la session).
    * pour cette même raison, il me semble que tous les objets déclarés ici ne devraient plus devraient être référencés une fois que la table est rechargée, ce qui est le cas chaque fois que le bean s'instancie (à moins que ce ne soit justement là que je commets une erreur de raisonnement?)
    * ta remarque préliminaire sur la référence qui tombe sous le sens est plus que légitime... mais je me demande si de ce côté là il n'y aurait pas du faite des technos utilisées (je pense notamment à Hibernate que j'avais cité à dessin) des objets qui échappent au traitement classique de la JVM et du GC (et qui seraient par exemple directement placés dans la zone permanente en court-circuitant l'Eden et toutes les zones intermédiaires).

    Mon autre souci est que malgré le fait que je constate (via le profiler) que de nombreux objets son instanciés alors que je suis sur une toute autre page, je ne sais finalement pas par qui ils sont instanciés (puisque - là encore a priori - le dataModel n'est plus instancié).

    Je n'ai pas compris par ailleurs ta suggestion concernant la classe comprenant "trop" d'éléments.. tu veux dire trop d'éléments (par ex. des AuctionItem) par rapport à ce qui devrait être affiché dans la table?

    En tous les cas, je continue les tests en m'inspirant de tes suggestions. Je reviens ici si j'ai plus de nouvelles, et dans tous les cas reste ouvert à toute suggestion.

  4. #4
    Membre averti Avatar de Shinzul
    Homme Profil pro
    Lecteur assidu de code source
    Inscrit en
    Janvier 2008
    Messages
    174
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Lecteur assidu de code source
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2008
    Messages : 174
    Points : 333
    Points
    333
    Par défaut
    Ok donc le coup du scope tombe a l'eau, on sais jamais il vaut mieux tester .

    Concernant le trop c'étais dans le cas d'un scope session, si les objets étaient chargés, vus que je voyais pas de clean ca pouvais être une piste.

    Avec un scope request normalement tes objets devrait se décharger a moins qu'il subsiste un lien vers eux dans une autres bean scopé Session (classe métier, dao ou autres).

    Pour ce qui est de Hibernate, je ne sais pas trop comment sont gérer les instances de caches et des entités remontées donc je ne peux pas te dire si le lien Factory (qui devrait être dans le permanent a ce niveau la :p) -> Gestionnaire de persistance fait que les instances chargés se place dans le permanent.

  5. #5
    Membre habitué
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2008
    Messages
    179
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Octobre 2008
    Messages : 179
    Points : 172
    Points
    172
    Par défaut
    Désolé pour le temps de réponse (pont oblige!).

    J'ai quelque observation supplémentaire depuis:
    tout semble venir du dataModel (donc le conteneur de mes objets) qui s'instancie à chaque rechargement de ma page sans éliminer les instances créées préalablement (le Garbage Collector n'y fait rien). Là encore, seule la fin de la session permet de les éliminer.

    Hors là je ne l'instancie pas explicitement, mais laisse l'injection de dépendances faire son travail :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    	<bean id="contactDataModel"
    		class="com.***.***.***.model.ContactDataModel"
    		scope="request" >
    		<property etc... />
    	</bean>
    Là par contre je cale, car je ne comprends pas pour quelle raison l'injection déraille ici, alors que par ailleurs elle se comporte normalement en ne maintenant qu'une instance de chaque objet injecté... (ce n'est évidemment pas la seule du projet)

    En tous les cas, la piste Hibernate s'éloigne (ce qui m'arrange du coup : voilà un cambouis où je suis ravi de ne pas mettre les mains!)

  6. #6
    Membre habitué
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2008
    Messages
    179
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Octobre 2008
    Messages : 179
    Points : 172
    Points
    172
    Par défaut
    On avance, on avance...
    Puisque une instance de l'objet ContactDataModel était créée à chaque Requête, un collègue a eu la lumineuse idée de transformer l'objet en singleton et du coup de court-circuiter l'injection de Spring (en récupérant directement l'instance dans le getter du bean dans lequel il était précédemment injecté).

    Singleton oblige, on régle de fait la multiplication incontrôlée des instances.
    Par contre, celà impose de transformer légèrement le principe du Singleton pour qu'il puisse être créé une instance par utilisateur (sinon plusieurs utilisateurs partageront le même singleton!). Donc ce n'est plus à proprement parlé un singleton (je ne sais pas si ça correspond à un pattern identifiable).

    Celà donne en terme de code du bean :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        public final ContactDataModel getContactDataModel() {
            return ContactDataModel.getInstance(getUserLogin(), contactService, userService);
        }
    Et dans celui du dataModel:
    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
        /**
         * default constructor
         */
        private ContactDataModel() {
        }
        
        public static ContactDataModel getInstance(String user, IContactService dataProvider, IUserService userService){
            if (instances == null){
                instances = new HashMap<String, ContactDataModel>();
            }
            if (instances.get(user) == null){
                ContactDataModel instance = new ContactDataModel();
                instance.setDataProvider(dataProvider);
                instance.setUserService(userService);
                instances.put(user, instance);
            }
            return instances.get(user);
        }
        
        /**
         * Clean useless object. Note that a bug in rich faces component
         * make it keep a reference on the ContactDataModel objects. So
         * these objects are not deleted.
         * @param user 
         */
        public static void cleanInstance(String user) {
            if (instances != null) {
                ContactDataModel instance = instances.get(user);
                if (instance != null){
                    instance.currentPk = null;
                    instance.filterMap = new HashMap<String, String>();
                    instance.wrappedKeys =  new ArrayList<Integer>();   
                    instance.wrappedData.clear();
                }
            }
        }
    Par contre, on a eu beau tout essayer : impossible de supprimer la dernière référence sur le singleton, à moins de détruire la session. Je pense de plus en plus à un bug de richfaces.
    Un autre symptôme observé qui va dans ce sens est la multiplication d'un objet richfaces (org.richfaces.model.selection.ClientSelection) qui suit le même schéma que le ContactDataModel. Hors de nombreux sujets pointent du doigt cet objet comme étant la cause de dépassement de mémoire. C'est même un bug déposé sur le site de Richfaces. Donc, il est possible que les deux problèmes soient liés... par contre notre outil de profilage (YourKit) n'est pas assez puissant pour retrouver le lien entre ces deux là (on dirait qu'il perd les pédales au moment de retrouver les relations de cet objet).

    Au final, on a utilisé quelques rustines, mais il reste un problème de fond. Je ne suis pas certain qu'il s'agisse bien du bug identifié lié au clientSelection, mais ça semble malgré tout une bonne piste!

    Si vous avez des avis ou des remarques, je suis preneur!
    Et merci encore de tes suggestions, Shinzul.

    Je ne marque pas ce sujet résolu car je ne pense pas que cette solution soit la meilleure. Je reviendrai ici si j'en ai une meilleure!

  7. #7
    Membre averti Avatar de Shinzul
    Homme Profil pro
    Lecteur assidu de code source
    Inscrit en
    Janvier 2008
    Messages
    174
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Lecteur assidu de code source
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2008
    Messages : 174
    Points : 333
    Points
    333
    Par défaut
    Donc le problème viens du réferencement des objets entre des instances Richfaces et de l'injection Spring.

    Effectivement ca tiens la route. Mais c'est effectivement pas terrible.
    Pour une autre solution, tu peux regarder au niveau d'un ThreadLocal si je me souviens bien chaque requête est traité indépendamment dans un thread ce qui pourrait correspondre a ton besoin initial.

  8. #8
    Membre habitué
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2008
    Messages
    179
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Octobre 2008
    Messages : 179
    Points : 172
    Points
    172
    Par défaut
    Je viens de jeter un oeil au ThreadLocal (que je ne connaissais absolument pas) et je ne suis pas tout à fait certain de comprendre..
    ton idée est de jumeler un singleton (un vrai cette fois) et le "thread Local" pour s'affranchir de la liaison entre l'instance et l'utilisateur, et laisser la technique du thread local décider à quel moment il est nécessaire d'en créer une autre instance?

  9. #9
    Membre averti Avatar de Shinzul
    Homme Profil pro
    Lecteur assidu de code source
    Inscrit en
    Janvier 2008
    Messages
    174
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Lecteur assidu de code source
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2008
    Messages : 174
    Points : 333
    Points
    333
    Par défaut
    Oui voila c'est un peu l'idée

    C'est juste pour te rapprocher du modèle que tu voulais à la base. Le fait de récupérer une instance par id d'utilisateur t'oblige a le traiter un peu comme une session (1 objet pour un utilisateur)

  10. #10
    Membre habitué
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2008
    Messages
    179
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Octobre 2008
    Messages : 179
    Points : 172
    Points
    172
    Par défaut
    J'ai creusé un peu plus dans ce sens, et j'ai un gros doute... j'ai l'impression que la durée de vie d'un threadLocal est limitée au thread : du coup, avec la règle 1 requête <=> 1 thread de JSF (si j'ai bien saisi), finalement je vais avoir autant d'instances que de requêtes?
    .. ou je n'ai finalement pas compris les "threadLocal"

  11. #11
    Membre averti Avatar de Shinzul
    Homme Profil pro
    Lecteur assidu de code source
    Inscrit en
    Janvier 2008
    Messages
    174
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Lecteur assidu de code source
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2008
    Messages : 174
    Points : 333
    Points
    333
    Par défaut
    C'est ca dans le même concept que si tu avais put garder ton ContactDataModel en scope request.

    A l'arrêt de ton thread ton objet sera bien retiré de la mémoire par le ThreadLocal, tu t'évite de devoir gérer la persistance en mémoire de ces instances. Au final avec la solution que tu donne tu as une instance par userId mais il faut aussi que tu puisse clean les instances lié à des user qui ne sont plus connecter pour éviter la surcharge de la mémoire .

Discussions similaires

  1. fuite de mémoire ?
    Par salseropom dans le forum C
    Réponses: 2
    Dernier message: 12/01/2006, 16h19
  2. Réponses: 1
    Dernier message: 02/12/2005, 14h18
  3. fuite de mémoire
    Par mamag dans le forum MFC
    Réponses: 17
    Dernier message: 19/08/2005, 10h42
  4. Fuite de mémoire en utilisant le template list
    Par schtroumpf_farceur dans le forum Langage
    Réponses: 9
    Dernier message: 18/07/2005, 20h44
  5. Réponses: 8
    Dernier message: 17/10/2002, 12h52

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