JVM (2) -Mecanismo de carga de clases-Principio de delegación parental

El subsistema de carga de la clase se introdujo anteriormente. Desde un archivo .class a una clase utilizable por la máquina virtual Java, debe cargarse -> enlace (verificación -> preparación -> análisis) -> inicialización -> uso -> descargar estos cinco procesos, después de estos cinco procesos, el archivo .class se convierte en una clase utilizable, pero específica del componente, ¿quién realiza los cinco procesos anteriores? ¿Quién garantiza la seguridad y precisión de este proceso de carga? Aquí tenemos que mencionar el cargador de clases y el modelo de delegación padre

Uno, el cargador de clases ClassLoader

Desde la perspectiva de la máquina virtual, existen dos tipos de cargadores de clases,①Inicie el cargador de clases, ②Otro cargadorLa base de esta división es que el cargador de clases de inicio está escrito en C / C ++, y los otros cargadores están escritos en lenguaje Java. Pero esta división es demasiado general.

Desde la perspectiva de los desarrolladores de Java, ClassLoader se divide en dos categorías:① El cargador que viene con la máquina virtual, ② Cargador de clases definido por el usuario

Cargador que viene con la máquina virtual

1.BootStrap ClassLoader

Cargador de clases de inicio BootStrap ClassLoader, el cargador de clases de inicio tiene las siguientes características:

  • Esta carga de clases está implementada en lenguaje C / C ++ y está anidada dentro de la JVM

  • Se utiliza para cargar la biblioteca central de Java (JAVA_HOME / jre / lib / rt.jar / resources.jar o el contenido de la ruta sun.boot.class.path) y se utiliza para proporcionar las clases que necesita la propia JVM.

  • No hereda de java.lang.ClassLoader, no hay un cargador principal

  • Cargar clases de extensión y cargador de clases de aplicación, y designarlos como su cargador principal

  • Por razones de seguridad, el cargador de clases de inicio de BootStrap solo carga clases cuyos nombres de paquete comienzan con java, javax, sun, etc.

2. Extensión ClassLoader

Extension ClassLoader expande el cargador de clases

  • Escrito en lenguaje Java e implementado por sun.misc.Launcher $ ExtClassLoader.

  • Derivado de la clase ClassLoader

  • El cargador de clases padre es el cargador de clases de inicio

  • Cargue la biblioteca de clases desde el directorio especificado por la propiedad del sistema java.ext.dirs, o cargue la biblioteca de clases desde el subdirectorio jre / lib / ext (directorio de extensión) del directorio de instalación de JDK. Si el JAR creado por el usuario se coloca en este directorio, el cargador de clases extendido también lo cargará automáticamente.

3.AppClassLoader

AppClassLoader , el cargador de clases de aplicaciones

  • Escrito en lenguaje Java e implementado por sun.misc.Launcher $ AppClassLoader.
  • Derivado de la clase ClassLoader
  • El cargador de clases padre es el cargador de clases extendido
  • Es responsable de cargar la biblioteca de clases en la ruta especificada por la variable de entorno classpath o la propiedad del sistema java.class.path

Este cargador de clases es el cargador de clases predeterminado en el programa . En términos generales, las clases de aplicaciones Java las carga

El cargador de clases se puede obtener a través del método ClassLoader # getSystemClassLoader ()

/**
 * @author 四五又十
 * @version 1.0
 * @date 2020/7/4 9:46
 */
public class demo2 {
    
    
    public static void main(String[] args) {
    
    

        //应用程序加载器  sun.misc.Launcher$AppClassLoader@18b4aac2
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //扩展类加载器  sun.misc.Launcher$ExtClassLoader@1540e19d
        ClassLoader exClassLoader = systemClassLoader.getParent();
        System.out.println(exClassLoader);

        //试图获取引导类加载器  null
        ClassLoader bootstrapClassLoader = exClassLoader.getParent();
        System.out.println(bootstrapClassLoader);
    }
}

A través del programa anterior, se puede encontrar que el valor obtenido por el cargador de clases de arranque Bootstrap ClassLoader es nulo, lo cual es fácil de entender, porque el cargador de clases de arranque está escrito en lenguaje C / C ++, por lo que naturalmente no está disponible. También se puede encontrar en el programa anterior que existe una relación jerárquica entre Bootstrap ClassLoader, Extension ClassLoader y AppClassLoader

Las características de estos 3 cargadores se presentan arriba. De hecho, la principal diferencia entre estos 3 cargadores es que,Cargar diferentes clases por separado:

AppClassLoader : responsable de cargar clases escritas por el usuario

Extension ClassLoader : Responsable de cargar las clases en el directorio <Java_Home> \ lib \ ext

BootStrap ClassLoader : responsable de cargar las bibliotecas de clases del núcleo de Java, el cargador de clases de inicio de BootStrap solo carga las clases cuyos nombres de paquete comienzan con java, javax, sun, etc.

Por ejemplo: intente obtener el cargador de clases que carga la clase String: BootStrap ClassLoader carga la clase String , luego la salida es nula

//输出 null
ClassLoader classLoader = String.class.getClassLoader();
System.out.println(classLoader);

Método para obtener ClassLoader:

1. Obtenga el ClassLoader de la clase actual: clazz.getClassLoader

2. Obtenga el ClassLoader del hilo actual: Thread.currentThread (). GetContextCladdLoader ()

3. Obtenga el cargador de clases del sistema: ClassLoader.getSystemClassLoader ()

4. Obtenga el cargador de clases de la persona que llama: DriverManager.getCallerClassLoader ()

4. Cargador definido por el usuario

  • Clase de carga de forma aislada
  • Modificar la forma en que se carga la clase
  • Expandir fuente de carga
  • Prevenir la fuga de código fuente

2. Mecanismo de delegación de los padres

Se utiliza un ejemplo para ilustrar los beneficios del mecanismo de delegación padre. Por ejemplo, cree una clase en el directorio de trabajo con el nombre de la clase y el nombre del paquete: java.lang.String. No es necesario hacer preguntas aquí. Algunas personas preguntarán que la clase String no está incluida en Java. ¿La clase? Esta creación es legal y el compilador no informará de un error. En la clase String, solo generamos una declaración como:

package java.lang;

/**
 * @author 四五又十
 * @version 1.0
 * @date 2020/7/6 18:29
 */
public class String {
    
    
    static {
    
    
        System.out.println("自定义String类");
    }
}

Luego vaya a un nuevo String en un método principal

public class demo4 {
    
    
    public static void main(String[] args) {
    
    
        String s = new String();
    }
}

Pensando en ello ahora, ¿la carga de la clase String cargará el String en el jdk, o nuestro método String personalizado, después de todo, todos están en el paquete java.lang? Si va a cargar la clase String en jdk, la consola no generará la salida. Si va a cargar la String personalizada, debido a que el bloque de código estático se usa en la clase String personalizada, la consola debe generar una oración "Clase de cadena personalizada" ?

¡La respuesta es que la consola no emite! ! Es decir, la máquina virtual Java elige cargar el String en jdk, que es el rol del modelo de delegación padre

El proceso de trabajo del mecanismo de delegación principal es: si un cargador de clases recibe una solicitud para cargar una clase, no intentará cargar la clase por sí mismo, sino que pasará la solicitud al cargador de clases principal, que no se puede cargar en él. se entrega al cargador de clases padre en un nivel superior, recursivamente a su vez, y solo cuando el cargador de clases padre no puede cargar la clase, el cargador hijo intentará cargarla.

De acuerdo con el modelo de delegación principal, se puede dibujar el proceso de carga de demo4 como arriba: el cargador de clases recibe la solicitud para cargar demo4, luego la máquina virtual le da la clase al cargador principal Extension ClassLoader , y no puedo cargar esta clase cuando miro la extensión ClassLoader . Luego se la doy al cargador de clases principal BootStrap ClassLoader , no se puede cargar BootStrap ClassLoader , por lo que todavía se delega hasta AppClassLoader , AppClassLoader es responsable de cargar las clases definidas por el usuario, se puede cargar, luego demo4 se carga en la máquina virtual Up.

Mire el proceso de carga de la clase String, primero AppClassLoader recibió una solicitud para cargar la clase String, AppClassLoader no intentará cargar, para ser entregado a la extensión ClassLoader principal , Extension ClassLoader una mirada que no tenía permiso para cargar , luego asigne una publicación en Para BootStrap ClassLoader , BootStrap ClassLoader ve que String está debajo del paquete java. La carga de esta clase pertenece al trabajo de BootStrap ClassLoader. Luego, la clase Stirng se carga en la máquina virtual. Luego, desde que se carga la clase String por BootStrap ClassLoader, entonces obviamente es String en jdk

En el código fuente, el código para implementar el mecanismo de delegación padre está en el método loadClass de java.lang.ClassLoader:

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

2.1 Beneficios del modelo de delegación parental

  • La relación jerárquica de prioridad estipulada por el modelo de delegación principal puede evitar la carga repetida de clases. Cuando el cargador principal ya ha cargado esta clase, si hay otra solicitud para cargar esta clase, entonces el cargador de clases de la aplicación pasará esta solicitud 1 al cargador de clases principal, y luego BootStrap inicia el cargador de clases para ejecutar el método findLoadedClass y encontrar La clase ya ha sido cargada, devolverá directamente que la clase no se está cargando, lo que evita la carga repetida de la clase
  • Otra ventaja del modelo de delegación padre es que evita que se modifiquen las clases principales. A partir del ejemplo anterior, puede saber que las clases principales generales se refieren a algunas clases en el paquete rt.jar o tools.jar. Si esta clase es maliciosamente modificada, se producirán consecuencias no deseadas, y este tipo de jerarquía de clases de carga del modelo de delegación parental asegura que la clase principal se cargue, porque la clase de inicio solo carga las clases en la ruta correcta

2.2 Rompiendo el modelo de delegación parental

El modelo de delegación parental asegura que las clases básicas de cada cargador de clases sean consistentes, es decir, estas clases básicas se entregan al cargador de clases superiores para su carga, pero esto también trae un problema. Hay muchas interfaces de llamada de núcleo de servicio (SPI para abreviar) en aplicaciones Java. Estas interfaces permiten que terceros lo implementen, como JDBC, etc. Estas interfaces SPI pertenecen a la biblioteca de clases principales y deben entregarse a la clase de inicio BootStrap Loader para cargar. Pero el problema es que las clases de implementación de estas interfaces son interfaces de terceros. Por lo anterior, sabemos que las bibliotecas de terceros deben entregarse al cargador de aplicaciones AppClassLoader para su carga. Debido a la existencia de delegación, Bootstrap no puede delegar AppClassLoader para cargar estas clases de implementación SPI.

Hay muchas interfaces de llamada de núcleo de servicio (SPI para abreviar) en la aplicación. Estas interfaces permiten que terceros lo implementen, como JDBC, etc. Estas interfaces SPI pertenecen a la biblioteca de clases de núcleo y deben ser cargadas por el cargador de clases de inicio de BootStrap , pero el problema Las clases de implementación que aparecen en estas interfaces son interfaces de terceros. Por lo anterior, sabemos que las bibliotecas de terceros deben entregarse al cargador de aplicaciones AppClassLoader para su carga. Debido a la existencia de delegación parental, Bootstrap no se puede delegar AppClassLoader para cargar estas clases de implementación de SPI.

Para resolver este problema, es necesario introducir algunos cargadores especiales de terceros, y el cargador de clases de contexto de subprocesos es un cargador de clases que rompe el mecanismo de delegación padre. Se introduce el cargador de contexto de subprocesos y el programa puede ser iniciado por The las clases cargadas por el cargador de clases son cargadas por el cargador personalizado, que se da cuenta de que el cargador de clases principal confía al cargador de subclase para cargar la clase, lo que resuelve el problema de que la clase principal no puede delegar la carga de la subclase bajo la delegación principal mecanismo

Supongo que te gusta

Origin blog.csdn.net/weixin_44706647/article/details/115140343
Recomendado
Clasificación