Análisis del código fuente del cargador de clases Java

1. Implementación de código fuente del cargador de clases

El mecanismo de carga de clases de Java y la creación de instancias de objetos han aclarado los tipos de cargadores de clases en Java. En pocas palabras, Java viene con tres cargadores de clases. Sus funciones específicas sonCargue dinámicamente el archivo de clase en la máquina virtual JVM según sea necesario. Las implementaciones correspondientes de estos tres cargadores de clases son las siguientes:

  1. Cargador de clases de arranque, clase de Bootstrap ClassLoader
    atributo del sistema de carga sun.boot.class.pathen la ruta especificada, está escrito por C / C ++, es parte de una máquina virtual, lo que no se puede lograr en el código java conocido
  2. Extienda el cargador de clases, Extension ClassLoader
    cargue java.ext.dirsla clase en la ruta especificada por la propiedad del sistema , la clase de implementación essun.misc.Launcher.ExtClassLoader
  3. Cargador de clases de aplicación, Application ClassLoader
    cargando la variable de entorno ClassPathen la biblioteca, la propiedad del sistema es java.class.pathuna ruta especificada, para la clase de implementaciónsun.misc.Launcher.AppClassLoader

Inserte la descripción de la imagen aquí

1.1 Cargador de clases abstracto Cargador de clases

java.lang.ClassLoader Es una abstracción del cargador de clases a nivel de Java. Esta clase regula el proceso básico de carga de clases. Las propiedades y métodos más importantes son los siguientes:

  1. parent: Una referencia al cargador de clases principal. Un cargador de clases generalmente guarda una referencia al cargador de clases principal para implementar el mecanismo de delegación principal.
  2. loadClass()Método, este método es el método principal del cargador de clases, que implementa la lógica de la delegación principal
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;

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
    
    
        synchronized (getClassLoadingLock(name)) {
    
    
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
    
    
                long t0 = System.nanoTime();
                try {
    
    
                    if (parent != null) {
    
    
                        c = parent.loadClass(name, false);
                    } else {
    
    
                        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();
                    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;
        }
    }

1.2 ExtClassLoader

sun.misc.Launcher.ExtClassLoader Es la clase de implementación del cargador de clases extendido. Los métodos más importantes de esta clase son los siguientes:

  1. getExtDirs()El método especifica la java.ext.dirsclase bajo la ruta especificada por su propiedad del sistema de carga
  2. Su método de construcción con parámetros llama al método de construcción de la clase principal, y el parámetro del cargador de clases principal pasado se especifica explícitamente como nulo, es decir, el cargador de clases extendido no tiene un cargador de clases principal de nivel Java.
static class ExtClassLoader extends URLClassLoader {
    
    
        private static volatile Launcher.ExtClassLoader instance;

        public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
    
    
            if (instance == null) {
    
    
                Class var0 = Launcher.ExtClassLoader.class;
                synchronized(Launcher.ExtClassLoader.class) {
    
    
                    if (instance == null) {
    
    
                        instance = createExtClassLoader();
                    }
                }
            }

            return instance;
        }

        private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {
    
    
            try {
    
    
                return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
    
    
                    public Launcher.ExtClassLoader run() throws IOException {
    
    
                        File[] var1 = Launcher.ExtClassLoader.getExtDirs();
                        int var2 = var1.length;

                        for(int var3 = 0; var3 < var2; ++var3) {
    
    
                            MetaIndex.registerDirectory(var1[var3]);
                        }

                        return new Launcher.ExtClassLoader(var1);
                    }
                });
            } catch (PrivilegedActionException var1) {
    
    
                throw (IOException)var1.getException();
            }
        }

        void addExtURL(URL var1) {
    
    
            super.addURL(var1);
        }

        public ExtClassLoader(File[] var1) throws IOException {
    
    
            super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
            SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
        }

        private static File[] getExtDirs() {
    
    
            String var0 = System.getProperty("java.ext.dirs");
            File[] var1;
            if (var0 != null) {
    
    
                StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
                int var3 = var2.countTokens();
                var1 = new File[var3];

                for(int var4 = 0; var4 < var3; ++var4) {
    
    
                    var1[var4] = new File(var2.nextToken());
                }
            } else {
    
    
                var1 = new File[0];
            }

            return var1;
        }
        
     ......
}

1.3 Cargador de clases de aplicaciones AppClassLoader

El cargador de clases de aplicaciones también se llamaCargador de clases de sistema, sun.misc.Launcher.AppClassLoaderEs su clase de implementación, los métodos más importantes de esta clase son los siguientes:

  1. getAppClassLoader()El método especifica la java.class.pathclase bajo la ruta especificada por su propiedad del sistema de carga
  2. Reescriba la loadClass()forma de hacer una carga optimizada de archivos de clase, si una clase existe en el directorio de la caché pero no en la memoria, si se lleva a cabo directamente 连接o si necesita un cargador de clases principal para cargar
static class AppClassLoader extends URLClassLoader {
    
    
        final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);

        public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
    
    
            final String var1 = System.getProperty("java.class.path");
            final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
            return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
    
    
                public Launcher.AppClassLoader run() {
    
    
                    URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
                    return new Launcher.AppClassLoader(var1x, var0);
                }
            });
        }

        AppClassLoader(URL[] var1, ClassLoader var2) {
    
    
            super(var1, var2, Launcher.factory);
            this.ucp.initLookupCache(this);
        }

        public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
    
    
            int var3 = var1.lastIndexOf(46);
            if (var3 != -1) {
    
    
                SecurityManager var4 = System.getSecurityManager();
                if (var4 != null) {
    
    
                    var4.checkPackageAccess(var1.substring(0, var3));
                }
            }

            if (this.ucp.knownToNotExist(var1)) {
    
    
                Class var5 = this.findLoadedClass(var1);
                if (var5 != null) {
    
    
                    if (var2) {
    
    
                        this.resolveClass(var5);
                    }

                    return var5;
                } else {
    
    
                    throw new ClassNotFoundException(var1);
                }
            } else {
    
    
                return super.loadClass(var1, var2);
            }
        }
    ......
}

2. La realización del mecanismo de delegación de padres

Inserte la descripción de la imagen aquí

La figura es un posible proceso de carga de clases, muchos pasos importantes en el proceso es nativeel método a lograr, no se pueden conocer los detalles de la capa de Java. El código del método principal de la clase de carga de la capa Java es el siguiente. En una breve descripción, incluye principalmente los siguientes pasos:

  1. Un AppClassLoadermomento para cargar la clase, primero vea si la clase se ha cargado antes, si se ha adquirido de la caché o si necesita delegar al cargador de clases principal para manejar
  2. ExtClassLoaderProcesos y AppClassLoadertotalmente coherentes, si no se había cargado la clase de destino, Bootstrap ClassLoaderpara realizar la carga lógica que está sun.mic.boot.classconfigurada se carga la ruta a la clase de destino, devuelve cargada, sin carga para que el niño encuentre su propio cargador
  3. Bootstrap ClassLoaderSi no se carga correctamente en la clase de destino, encontrará la clase de destino para cargar ExtClassLoaderen la java.ext.dirsruta configurada, regresará si tiene éxito y luego dejará que el subcargador cargue la clase si falla
  4. ExtClassLoaderLa clase de carga no tuvo éxito, AppClassLoaderla java.class.pathclase de destino de búsqueda de la configuración de la ruta, la carga regresó para encontrarla. Si no lo encuentra, deje que el cargador de subclase lo encuentre, si no hay subclase, se lanzarán varias excepciones
 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
    
    
        synchronized (getClassLoadingLock(name)) {
    
    
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name); // 1.首先判断类是否已经被加载
            if (c == null) {
    
     // 2. 未被加载则进行双亲委派加载
                long t0 = System.nanoTime();
                try {
    
    
                    if (parent != null) {
    
    
                        c = parent.loadClass(name, false);
                    } else {
    
    
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
    
    
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
    
     //3.父类加载器 loadClass 未加载到类才调用 findClass
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    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;
        }
    }

Supongo que te gusta

Origin blog.csdn.net/weixin_45505313/article/details/106547155
Recomendado
Clasificación