ClassLoader simple y fácil de entender

El cargador de clases de Java es un componente del entorno de ejecución de Java, que es responsable de cargar de forma dinámica las clases de Java en el espacio de memoria de la máquina virtual Java. Las clases generalmente se cargan bajo demanda, es decir, se cargan cuando la clase se usa por primera vez. Gracias al cargador de clases, el sistema de ejecución de Java no necesita conocer archivos ni sistemas de archivos. Para aprender el cargador de clases, es muy importante dominar el concepto de delegación en Java. Cada clase Java debe ser cargada en la memoria por algún cargador de clases. (Wikipedia)

Cargador de clases:

Hay dos tipos de cargadores de clases: cargador de clases de arranque proporcionado por java, máquina virtual y cargador de clases definido por el usuario, cada cargador de clases definido por el usuario es una subclase de ClassLoader

  • Bootstrap (responsable de cargar las bibliotecas de clases centrales en el JDK, como: rt.jar, resource.jar, charsets.jar, etc., implementado en C ++)
  • Extensión (responsable de cargar el paquete jar extendido jre / lib / ext / *. Jar o especificado por -Djava.ext.dirs)
  • Aplicación (carga el contenido especificado por classpath)
  • personalizado (Cargador de clases personalizado)
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

Lanzacohetes

Launcher es la clase utilizada para iniciar la entrada del programa main () en JRE, veamos el código 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);
	        }
        //删除无关代码
	}
}	

¿No vio Bootstrap, porque está implementado en c ++?
String bootClassPath = System.getProperty ("sun.boot.class.path"); Puede ver la ruta de carga inicial
final String var1 = System.getProperty ("java.class .path ”); // appclassloader cargando URL
String var0 = System.getProperty (" java.ext.dirs "); // La URL de carga de ExtClassLoader
ahora está probando cuáles están cargadas:

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

Cargador principal

Cada clase tiene un cargador principal, el primer programa también vio que se puede obtener a través de Classloader.getParent (). Con especial énfasis aquí no es la herencia padre-hijo , pero no hereda cómo puede llegar a ella, el cargador de clases se hereda URLClassLoader esta clase pero esta clase es inútil dentro de getParent () Este método es, de hecho, en el código fuente de Classloader, puede ver:

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;
        }
    }

Un atributo de Classloader es parent, que especifica claramente el cargador principal del cargador actual. Hay dos asignaciones para el principal:

  • Especifique directamente un ClassLoader como padre cuando el ClassLoader es creado por la clase externa en el constructor.
  • Generado por el método getSystemClassLoader (), que se obtiene mediante getClassLoader () en sun.misc.Laucher, que es AppClassLoader. Para decirlo sin rodeos, si se crea un ClassLoader sin especificar un padre, entonces su padre es AppClassLoader por defecto.
    En el código de Laucher que publiqué arriba, se puede ver que la aplicación en el método de construcción de Laucher se copia en el cargador, y el cargador es ClassLoader.

¡Así que deja de hablar de herencia!

Delegación de padres

Inserte la descripción de la imagen aquí

No es demasiado tarde para publicar esta imagen aquí. Oye, puedes compararla con la de arriba.

Ahora echemos un vistazo a cómo se carga la clase Class, es decir, a través del proceso de delegación de los padres a llamadas recursivas similares, busque capa por capa y luego publique una imagen: Inserte la descripción de la imagen aquí
ahora hablemos sobre el proceso de carga, en primer lugar, un sufijo .class Mira hasta la capa de la clase a capa , se puede ver que en la imagen anterior, la CustomClassLoader primero va a buscar en la memoria caché para encontrar el caché para ver si se ha cargado. Si es así, se devolverá el resultado directamente. Si no, se le notificará al gestor de los padres. Vaya a buscar en el caché y devuelva el resultado si no lo está, continúe con la operación anterior. Vaya al cargador de clases Ext para buscar en el caché para encontrar el caché. Si se devuelve, vaya al Bootstrap de nivel superior para encontrarlo. De lo contrario, primero intentará cargar findclass esta clase no es responsable de su propia carga (dicho arriba Bootstrap que es responsable de la carga), luego baja para dejar que el niño vaya a Ext load, encuentra que no está cargada, capa por capa hasta la última costumbre son De lo contrario, aparecerá la familiar ClassNotFindException. Este es el proceso de carga aproximado. Puedes mirar el código fuente 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);
    }
  }  

El código explica razonablemente el proceso de delegación de los padres.¡La delegación de los padres en realidad significa que el niño delega al padre y luego el padre delega al niño!
El método loadClass es muy importante, debes implementar este método si quieres personalizarlo.

Cargador de clases personalizado

¡La personalización solo necesita implementar loadClass ()! Ir directamente al código

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

De hecho, se hace leyendo el archivo en la matriz de bytes y luego convirtiendo la matriz de bytes en un archivo de clase, porque sabemos que siempre que pueda convertir el archivo en una clase, puede ejecutarlo en la JVM. Este trabajo es el defineClass anterior. () El método está hecho.

Ya casi ha terminado, por supuesto que hay más cuestiones esotéricas, pero hay una capacidad limitada.

Supongo que te gusta

Origin blog.csdn.net/qq_45422703/article/details/109091005
Recomendado
Clasificación