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 :

"Décharger" une classe java avec un ClassLoader Custo


Sujet :

Langage Java

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    147
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 147
    Points : 66
    Points
    66
    Par défaut "Décharger" une classe java avec un ClassLoader Custo
    Bonjour à tous,

    Je désire "décharger" (avec le ramasse-miettes) une classe java charger par un classloader.
    J'ai créé un classloader custo.

    Le chargement de la classe :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    ClassLoader classLoader = new MyClassLoader();
    Class<?> classACharger = Class.forName(nom, true, classLoader);//nom = String nommant la classe à charger
    classACharger.newInstance();
    Le déchargement qui ne fonctionne pas (fonction retirerUneClasse):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    classesCharges.remove(nom);//HashSet<String> contenant l'ensemble des nom de classes chargées 
    	  ChargeurI nouveauChargeurDeClass = new ChargeurImpl();
    	  for (String nomClass : classesCharges) {
    		  nouveauChargeurDeClass.chargerUneClasse(nomClass);
    	  }
    	  return nouveauChargeurDeClass;

    Le test :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ClassLoadingMXBean  classLoading = ManagementFactory.getClassLoadingMXBean();
        ChargeurI cc = new ChargeurImpl();
        cc.chargerUneClasse("package1.class1");
        cc.chargerUneClasse("package1.class2");
     
        ChargeurDeClassesI cc1 = cc.retirerUneClasse("package1.class1"); 
     
        classLoading.setVerbose(true);
        cc=null; System.gc();
        System.out.println("Nb classes déchargées : " + classLoading.getUnloadedClassCount());
    Le test affiche donc : "Nb classes déchargées : 0"

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

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    deux choses

    1) ton code de déchargement ne sert à rien, en effet, après chargement (en supposant que tu utiliser les classes que tu charge, ce qui n'est pas le cas dans ton exemple) tu aura ceci comme arbre de références
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class 1 <---->classloader1 <---> class 2
        ^              ^               ^
        |              |               |
        +---------   Main -------------+
    et après le "retireruneClasse(class1)", tu aurais ceci

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    class 1 <---->classloader1 <---> class 2
                                       ^
                                       |
                    Main --------------+
                      |
                      v
                 classloader2  <---> class2bis
    Tu notera que paradoxalement, on a fait que rajouter des choses.
    En effet, le seul moyen de garbage collecter une classe, c'est que son classloader soit garbage collecté. Et celui-ci ne peut l'etre que si il n'est plus référencé nulle part et si aucune des classes qui l'a chargée n'est référencée ailleurs (donc ni par des références directes classe<?>, ni par des instances, ni par des références indirectes aux instances, etc, regles habituelles de gc).

    2) tu appelle gc, mais les appel gc ne te garantissent pas que tout a été gc (voir la doc de System.gc()) -> suggestion: une boucle de gc() ou un boucle de new qui consomme de la mémoire

    3) on ne vois pas le code de ton classloader. Comment charge-t-il les classes? En effet, si il utilise betement le modèle de déléguation, tu va avoir ce shéma là au chargement des classes:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class 1 <---->System classloader <---> class 2
        ^               ^                    ^
        |               |                    |
        |           classloader1             |
        |               |                    |
        +------------- Main -----------------+

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    147
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 147
    Points : 66
    Points
    66
    Par défaut
    Je te remercie pour ces explications clair et schématisées

    Citation Envoyé par tchize_ Voir le message
    En effet, le seul moyen de garbage collecter une classe, c'est que son classloader soit garbage collecté. Et celui-ci ne peut l'etre que si il n'est plus référencé nulle part et si aucune des classes qui l'a chargée n'est référencée ailleurs (donc ni par des références directes classe<?>, ni par des instances, ni par des références indirectes aux instances, etc, regles habituelles de gc).
    Je sais bien, c'est justement ce que je recherche à faire

    Tu pense donc qu'aprés avoir utilisé la fonction "retireruneClasse(class1)", la classe "class 2" sera tjrs référencée par la classe de test "Main" ?
    Mon but était le suivant :
    1 - classloader1 : class1 + class2 (classes chargées)
    2 - retirer "class1" :
    classloader1 : class1 + class2
    création du ClassLoader classloader2
    classloader2 : class2
    3 - Déférencer classloader1
    4 - Utilisation du GC


    Je détail un peu plus mon code :

    Le chargeur/déchargeur de classe :
    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
    import java.util.HashSet;
    import java.util.Set;
     
    public class ChargeurDeClassesImpl implements ChargeurDeClassesI{
     
     private Set <String> classesCharges = new HashSet<String>();
     private ClassLoader classLoader;
     
     public ChargeurDeClassesImpl(){
    	 /*
    	  * Ce code créé un chargeur de classes, ce qui nécessite un test de sécurité. 
    	  * Même si ce code possède des droits suffisants, la création du chargeur de classes 
    	  * devrait être effectuée dans un block doPrivileged au cas ou il serait appelé par 
    	  * du code ne possédant pas les droits requis.
    	  */
    	 classLoader = new MyClassLoader();
     }
     
     /**
      * Charge à la demande un fichier ".class" et instancie la classe Class<?> correspondante. 
      * @param nom Nom de la classe à charger.
      * @return Classe chargée.
      */
      public Class<?> chargerUneClasse(String nom) throws Exception{
    	if(nom == null)return null;
    	//true (utilisation de la méthode resolve du classloader) : permet de charger les classes utilisés par la classe "nom" à charger.
    	//classLoader = Thread.currentThread().getContextClassLoader() ;
    	//classLoader = java.lang.ClassLoader.getSystemClassLoader() ;
    	Class<?> classACharger = Class.forName(nom, true, classLoader);
    	//Class<?> classACharger = classLoader.loadClass(nom);
     
    	if(classACharger==null) return null;
    	classACharger.newInstance();
    	classesCharges.add(nom);
    	return classACharger;
      }
     
    /**
       * Décharge la classe passée en paramétre.
       * Le déchargement effectif des classes est laissé à l'initiative du ramasse-miettes.
       * Le ramasse-miettes libère l'espace mémoire utilisé par le chargeur de classes auquel certaines classes 
       * auront été retirées et retire implicitement les instances de la classe Class<?> qui ne sont plus référencées.
       * 
       * @param nom Nom de la classe à décharger.
       * @return Le chargeur de classe.
       */
      public ChargeurDeClassesI retirerUneClasse(String nom) throws Exception{
        // à compléter
    	  if(nom == null)return null;
    	  classesCharges.remove(nom);
     
    	  ChargeurDeClassesI nouveauChargeurDeClass = new ChargeurDeClassesImpl();
    	  for (String nomClass : classesCharges) {
    		 // Class.forName(nom, true,java.lang.ClassLoader.getSystemClassLoader() ).
    		  nouveauChargeurDeClass.chargerUneClasse(nomClass);
    	  }
    	  //classLoader = null;
    	  return nouveauChargeurDeClass;
          //return this;
      }
    Le test (classe Main):
    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
    import java.lang.management.*;
    import javax.management.*;
     
    public class Main{
     
      public static void main(String[] args)throws Exception{
        ClassLoadingMXBean  classLoading = ManagementFactory.getClassLoadingMXBean();
    classLoading.setVerbose(true);  // pour le vérifier à la console
     
         //System.out.println("<classLoading.getLoadedClassCount(),classLoading.getTotalLoadedClassCount(),getUnloadedClassCount()>");
         //System.out.println("<" + classLoading.getLoadedClassCount() + "," + classLoading.getTotalLoadedClassCount()+ "," + classLoading.getUnloadedClassCount() + ">"); 
     
        ChargeurDeClassesI cc = new ChargeurDeClassesImpl();
        cc.chargerUneClasse("question1.A");
        cc.chargerUneClasse("question1.C");  //  2 classes chargées : A et C
     
        ChargeurDeClassesI cc1 = cc.retirerUneClasse("question1.C"); 
     
        cc=null; System.gc();
        //  2 classes "déchargées" par le ramasse-miettes : A et C, de cc
        System.out.println("UnloadedClassCount() : " + classLoading.getUnloadedClassCount());
       }
    }
    Le loader custo:
    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
    import java.io.*; 
    import java.util.*;
     
    public class MyClassLoader extends ClassLoader{ 
       private Map<String,Class<?>> classes;
     
       public MyClassLoader(){
            super(MyClassLoader.class.getClassLoader());
            classes = new HashMap<String,Class<?>>();
        }
     
        protected synchronized Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException{ 
          Class <?> classe = classes.get(name); //  ou bien sans classes, Class classe = findLoadedClass(name);
          if (classe == null) {  
             byte[] classBytes = loadClassBytes(name);
             if (classBytes == null){
                return findSystemClass(name);
             }
     
             classe = defineClass(name, classBytes, 0, classBytes.length);
             if (classe == null)
                throw new ClassNotFoundException(name);
             classes.put(name, classe); 
          }
          if (resolve) resolveClass(classe);
     
          return classe;
       }
     
       private byte[] loadClassBytes(String name){  
    	   String cname = name.replace('.', '/') + ".class";
         FileInputStream in = null;
         try{  
    		   in = new FileInputStream(cname);
           ByteArrayOutputStream buffer = new ByteArrayOutputStream();
           int ch;
           while ((ch = in.read()) != -1){
    			   byte b = (byte)(ch);
             buffer.write(b);
           }
           in.close();
           return buffer.toByteArray();
         }catch (IOException e){
    	     if (in != null){
    	       try {in.close(); } catch (IOException e2) { }
         }
         return null;
       }
      }
    }
    Ce classloader te semble-t-il approprié ?

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    147
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 147
    Points : 66
    Points
    66
    Par défaut
    En tout cas merci pour cette réponse
    Malheureusement, mon prb n'est tjrs pas résolu ...

Discussions similaires

  1. Compilation d'une classe java avec cmd appelant une autre classe
    Par y_chafaqi dans le forum Général Java
    Réponses: 10
    Dernier message: 09/12/2009, 20h24
  2. Réponses: 1
    Dernier message: 24/08/2009, 13h09
  3. compiler une classe java avec javac
    Par crespoo dans le forum Débuter avec Java
    Réponses: 8
    Dernier message: 20/04/2009, 18h32
  4. Créer une classe .java avec un code java
    Par demcoul dans le forum Langage
    Réponses: 10
    Dernier message: 03/01/2008, 00h06

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