Simple et facile à comprendre, ClassLoader

Le chargeur de classe Java est un composant de l'environnement d'exécution Java, qui est responsable du chargement dynamique des classes Java dans l'espace mémoire de la machine virtuelle Java. Les classes sont généralement chargées à la demande, c'est-à-dire qu'elles sont chargées lors de la première utilisation de la classe. Grâce au chargeur de classe, le système d'exécution Java n'a pas besoin de connaître les fichiers et les systèmes de fichiers. Pour apprendre le chargeur de classe, il est très important de maîtriser le concept de délégation en Java. Chaque classe Java doit être chargée en mémoire par un chargeur de classe. (Wikipédia)

Chargeur de classe:

Il existe deux types de chargeurs de classe: le chargeur de classe de démarrage fourni par java, la machine virtuelle et le chargeur de classe défini par l'utilisateur, chaque chargeur de classe défini par l'utilisateur est une sous-classe de ClassLoader

  • Bootstrap (responsable du chargement des bibliothèques de classes principales dans le JDK, telles que: rt.jar, resource.jar, charsets.jar, etc., implémentées en C ++)
  • Extension (responsable du chargement du package jar étendu jre / lib / ext / *. Jar ou spécifié par -Djava.ext.dirs)
  • App (charger le contenu spécifié du chemin de classe)
  • personnalisé (ClassLoader personnalisé)
public static void main(String[] args) throws ClassNotFoundException {
    
    

        //String java 核心类库 ---> null 由顶级BootStrap加载 C++实现 打印为null=BootStrap
        System.out.println(String.class.getClassLoader());
        //Main 当前的这个类  ---->sun.misc.Launcher$AppClassLoader@18b4aac2是加载classPath路径下的
        System.out.println(Main.class.getClassLoader());
        //获取 AppClassLoader的父加载器--->ExtClassLoader
        System.out.println(Main.class.getClassLoader().getParent());
        //获取 AppClassLoader的父加载器的父加载--->BootStrap ->null
        System.out.println(Main.class.getClassLoader().getParent().getParent());
        
        Class<?> aClass = Class.forName("java.lang.ClassLoader");
        System.out.println(aClass.getClassLoader());
    }
    输出结果:
    null
	sun.misc.Launcher$AppClassLoader@18b4aac2
	sun.misc.Launcher$ExtClassLoader@1b6d3586
	null
	null

Lanceur

Launcher est la classe utilisée pour démarrer l'entrée de programme main () dans JRE, regardons le code de Launcher

public class Launcher {
    
    
	 private ClassLoader loader;
	public Launcher() {
    
    
	        Launcher.ExtClassLoader var1;
	        try {
    
    
	        	//加载ExtClassLoader
	            var1 = Launcher.ExtClassLoader.getExtClassLoader();
	        } catch (IOException var10) {
    
    
	            throw new InternalError("Could not create extension class loader", var10);
	        }
	
	        try {
    
    
	        	//加载AppClassLoader
	            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
	        } catch (IOException var9) {
    
    
	            throw new InternalError("Could not create application class loader", var9);
	        }
        //删除无关代码
	}
}	

Vous n’avez pas vu Bootstrap, car il est implémenté en c ++;
String bootClassPath = System.getProperty ("sun.boot.class.path"); Vous pouvez voir le chemin de chargement initial
final String var1 = System.getProperty ("java.class .path ”); // appclassloader loading URL
String var0 = System.getProperty (" java.ext.dirs "); // L'URL de chargement d'ExtClassLoader
teste maintenant celles qui sont chargées:

public static void main(String[] args){
    
    
 		String pathBoot = System.getProperty("sun.boot.class.path");
        System.out.println(pathBoot.replaceAll(";",System.lineSeparator()));
        
        System.out.println("--------------------");
        String pathExt = System.getProperty("java.ext.dirs");
        System.out.println(pathExt.replaceAll(";", System.lineSeparator()));

        System.out.println("--------------------");
        String pathApp = System.getProperty("java.class.path");
        System.out.println(pathApp.replaceAll(";", System.lineSeparator()));
}
本机输出结果:
"D:\Program Files\Java\jdk1.8.0_261\bin\java.exe"
D:\Program Files\Java\jdk1.8.0_261\jre\lib\resources.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\rt.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\sunrsasign.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jsse.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jce.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\charsets.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jfr.jar
D:\Program Files\Java\jdk1.8.0_261\jre\classes
--------------------
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext
C:\Windows\Sun\Java\lib\ext
--------------------
D:\Program Files\Java\jdk1.8.0_261\jre\lib\charsets.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\deploy.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\access-bridge-64.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\cldrdata.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\dnsns.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\jaccess.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\jfxrt.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\localedata.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\nashorn.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunec.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunjce_provider.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunmscapi.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\sunpkcs11.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\ext\zipfs.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\javaws.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jce.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jfr.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jfxswt.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\jsse.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\management-agent.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\plugin.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\resources.jar
D:\Program Files\Java\jdk1.8.0_261\jre\lib\rt.jar
D:\java\code\jvm\out\production\jvm
D:\Program Files\JetBrains\IntelliJ IDEA 2020.2.1\lib\idea_rt.jar

Chargeur parent

Chaque classe a un chargeur parent, le premier programme a également vu qu'il peut être obtenu via Classloader.getParent (). Avec un accent particulier ici n'est pas l'héritage père-fils , mais pas hériter comment vous pouvez y accéder, le chargeur de classe est hérité de URLClassLoader cette classe mais cette classe est inutile dans getParent () Cette méthode est, en fait, dans le code source de Classloader, vous pouvez voir:

public abstract class ClassLoader {
    
    
    // The parent class loader for delegation
    // Note: VM hardcoded the offset of this field, thus all new fields
    // must be added *after* it.
    private final ClassLoader parent;
    
    }
    //构造方法
    private ClassLoader(Void unused, ClassLoader parent) {
    
    
        this.parent = parent;
        if (ParallelLoaders.isRegistered(this.getClass())) {
    
    
            parallelLockMap = new ConcurrentHashMap<>();
            package2certs = new ConcurrentHashMap<>();
            assertionLock = new Object();
        } else {
    
    
            // no finer-grained lock; lock on the classloader instance
            parallelLockMap = null;
            package2certs = new Hashtable<>();
            assertionLock = this;
        }
    }

L'un des attributs de Classloader est parent, qui spécifie clairement le chargeur parent du chargeur actuel. Il existe deux affectations pour le parent:

  • Spécifiez directement un ClassLoader comme parent lorsque le ClassLoader est créé par la classe externe dans le constructeur.
  • Généré par la méthode getSystemClassLoader (), qui est obtenue par getClassLoader () dans sun.misc.Laucher, qui est AppClassLoader. Pour le dire franchement, si un ClassLoader est créé sans spécifier de parent, alors son parent est AppClassLoader par défaut.
    Dans le code de Laucher que j'ai posté ci-dessus, on peut voir que l'application dans la méthode de construction de Laucher est copiée sur le chargeur et que le chargeur est le ClassLoader.

Alors arrêtez de parler d'héritage!

Délégation des parents

Insérez la description de l'image ici

Il n'est pas trop tard pour publier cette photo ici. Vous pouvez la comparer à celle ci-dessus.

Voyons maintenant comment la classe Class est chargée, c'est-à-dire à travers le processus de délégation parentale à des appels récursifs similaires, cherchons couche par couche puis publions une image: parlons Insérez la description de l'image ici
maintenant du processus de chargement, tout d'abord, un suffixe .class la recherche jusqu'à la couche de classe par couche , vous pouvez voir l'image ci - dessus. tout d'abord, le CustomClassLoader va d' abord trouver en cache pour trouver le cache pour voir si elle a été chargée. en cas, il retourne directement le résultat. dans le cas contraire, il avisera le chargeur de parent. Allez chercher dans le cache et renvoyez le résultat s'il n'y en a pas, continuez avec l'opération précédente. Accédez au chargeur de classe Ext pour rechercher dans le cache pour trouver le cache. S'il est renvoyé, accédez au Bootstrap de niveau supérieur pour le trouver. Sinon, il essaiera de charger findclass lui-même. cette classe ne se trouve pas responsable de sa propre charge (dit ci-dessus Bootstrap qui est responsable du chargement), puis en bas pour laisser l'enfant aller Ext load, elle constate qu'elle n'est pas chargée, couche par couche jusqu'à la dernière personnalisation Sinon, l'exception ClassNotFindException familière apparaîtra. Il s'agit du processus de chargement approximatif. Vous pouvez consulter le code source ClassLoader.loadClass ():

 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
    
    	
    
        synchronized (getClassLoadingLock(name)) {
    
    
            // First, check if the class has already been loaded
            //1.检查类是否已经被加载了 就是前面的缓存啦
            Class<?> c = findLoadedClass(name);
            if (c == null) {
    
    
                long t0 = System.nanoTime();
                try {
    
    
                    if (parent != null) {
    
    
                    	//2.没有就通知父类加载器去加载啦
                    	// 这里就是递归调用loadClass直到最
                    	//顶级的时候parent不就等于null了吗
                        c = parent.loadClass(name, false);
                    } else {
    
    
                    	//parent==null就BootstrapClass去加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
    
    
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
					
                if (c == null) {
    
    
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //3.如果都是空的话呢 就自己去找咯 
                    //直到自己定义的类加载还没有找到的话就会报
                    //ClassNotFindException
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
    
    
                resolveClass(c);
            }
            return c;
        }
        //子类可以实现,模板方法模式
         protected Class<?> findClass(String name) throws ClassNotFoundException {
    
    
        throw new ClassNotFoundException(name);
    }
  }  

Le code explique raisonnablement le processus de délégation parentale: la délégation parentale signifie en fait que l'enfant délègue au père, puis le père délègue à l'enfant!
La méthode loadClass est très importante. Vous devez implémenter cette méthode si vous souhaitez la personnaliser.

Chargeur de classe personnalisé

La personnalisation n'a besoin que d'implémenter loadClass ()! Aller directement au code

class MyClassLoader extends ClassLoader{
    
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
    
    

        File f = new File("D:/java/code/jvm/", name.replace(".", "/").concat(".class"));
        try {
    
    
            FileInputStream fileInputStream = new FileInputStream(f);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int b=0;
            while ((b=fileInputStream.read())!=0){
    
    
                byteArrayOutputStream.write(b);
            }
            byte[] bytes = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.close();
            fileInputStream.close();//可以写的更加严谨
            return defineClass(name, bytes, 0, bytes.length);
        } catch (FileNotFoundException e) {
    
    
            e.printStackTrace();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
        return super.findClass(name);
    }
}

class HelloClassLoader{
    
    
    static {
    
    
        System.out.println("static");
    }
    public void Hello(){
    
    
        System.out.println("Hello");
    }
    public void m(){
    
    
        System.out.println("Hello Word!");
    }
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
    
    

        ClassLoader myClassLoader = new MyClassLoader();
        Class<?> clazz = myClassLoader.loadClass("com.yedi.Hello");
        Hello hello = (Hello) clazz.newInstance();

        System.out.println(hello);

        hello.m();

        Hello hello1 = new Hello();

        System.out.println(hello1);

    }
    输出结果:
    static
	Hello
	com.yedi.Hello@4554617c
	Hello Word
	Hello
	com.yedi.Hello@74a14482

En fait, cela se fait en lisant le fichier dans le tableau d'octets, puis en convertissant le tableau d'octets en fichier de classe, car nous savons que tant que vous pouvez convertir le fichier en classe, vous pouvez l'exécuter sur la JVM. Ce travail est le defineClass ci-dessus. () La méthode est terminée.

Bien sûr, il y a plus de problèmes ésotériques, mais les capacités sont limitées.

Je suppose que tu aimes

Origine blog.csdn.net/qq_45422703/article/details/109091005
conseillé
Classement