Hable sobre el mecanismo de carga de clases Java

Las entrevistas de Java a menudo preguntan cuál es el mecanismo de carga de clases de Java. Hoy presentaremos el cargador de clases de Java y el proceso de carga de clases. Permítanme hablar sobre el proceso de carga de clases.

Las preguntas de la entrevista de Java a menudo preguntan cuál es el proceso de carga de la clase, o al darle un programa y responder a la salida del programa Estos puntos de conocimiento están relacionados con el proceso de carga de la clase de Java. Hable sobre los 7 procesos de carga de clases.

Proceso de carga de clase

Cargando

Cargue el archivo de clase en la memoria y cree el objeto de clase correspondiente en el área de método

Verificar

Compruebe si el archivo de clase cargado se ajusta a la especificación de bytecode

Listo

  • Después de completar la fase de verificación, jvm comienza a asignar memoria para las variables de clase e inicializar valores cero.
  • Las "variables de clase" se refieren a variables modificadas por static.
  • En la etapa de preparación, la JVM solo asignará memoria para "variables de clase", no para "variables de miembro de clase".
  • Pero si una variable es una constante (modificada por la estática final), en la etapa de preparación, el atributo recibirá el valor deseado del usuario. Porque el valor modificado por final no se puede cambiar una vez que se asigna.

Analizando

La JVM resuelve 7 tipos de referencias a clases o interfaces, campos, métodos de clase, métodos de interfaz, tipos de métodos, identificadores de métodos y calificadores de puntos de llamada. La tarea principal de esta etapa es reemplazar su referencia de símbolo en el grupo constante con su referencia directa en la memoria.

Inicializar

En la etapa de inicialización, el código del programa Java definido por el usuario realmente comienza a ejecutarse. En esta etapa, la JVM inicializará los objetos de clase de acuerdo con el orden de ejecución de las declaraciones. En términos generales, cuando la JVM encuentra las siguientes 5 situaciones, activará la inicialización:

  • Al encontrar las cuatro instrucciones de bytecode new, getstatic, putstatic, invookestatic, si la clase no se ha inicializado, primero debe activar su inicialización. El escenario de código Java más común que genera estas 4 instrucciones es: cuando se usa la nueva palabra clave para crear una instancia de un objeto, leer o establecer un campo estático de una clase (decorada por final, la estática que el compilador ha puesto en el grupo constante) Excepto para los campos), y al llamar a métodos estáticos de una clase.
  • Al utilizar los métodos del paquete java.lang.reflect para hacer una llamada reflexiva a una clase, si la clase no se ha inicializado, primero debe activar su inicialización.
  • Al inicializar una clase, si encuentra que su clase principal no se ha inicializado, primero debe activar la inicialización de su clase principal.
  • Cuando se inicia la máquina virtual, el usuario debe especificar una clase principal que se ejecutará (la que contiene el método main ()), y la máquina virtual primero inicializa esta clase principal.
  • Cuando se utiliza el soporte de lenguaje dinámico JDK1.7, si el resultado del análisis final de una instancia de método ja_.lang.invoke.MethodHandle REF_getstatic, REF_putstatic, REF_invokeStatic, y la clase correspondiente a este identificador de método no se inicializa, debe activar Su inicialización.

Para utilizar

Después de que la JVM completa la fase de inicialización, la JVM comienza a ejecutar el código del programa del usuario desde el método de entrada.

Desinstalar

Una vez que se completa la ejecución del código del programa de usuario, la JVM comienza a destruir el objeto de clase creado, y finalmente la JVM responsable de la ejecución también sale de la memoria.

Resumen de carga de clase

Puede verse en los ejemplos anteriores que analizar el orden de ejecución de una clase puede seguir aproximadamente los pasos a continuación:

  • Determine el valor inicial de la variable de clase. En la etapa de preparación de la carga de clases, la JVM inicializa el valor cero de la variable de clase, en este momento, la variable de clase tendrá un valor cero inicial. Si se trata de una variable de clase modificada por final, se inicializará directamente al valor que el usuario desee.
  • Inicializar el método de entrada. Al ingresar a la fase de inicialización de la carga de la clase, la JVM buscará toda la entrada del método principal, inicializando así toda la clase donde reside el método principal. Cuando una clase necesita ser inicializada, la clase constructor () se inicializa primero, seguido por el constructor de objetos ().
  • Inicializa el constructor de la clase. La JVM recopilará las instrucciones de asignación y los bloques de código estático de las variables de clase en orden, y finalmente formará el constructor de clase que será ejecutado por la JVM.
  • Inicializa el constructor de objetos. La JVM recopilará las declaraciones de asignación y los bloques de código ordinarios de las variables miembro, y finalmente recopilará los métodos de construcción para formarlos en un constructor de objetos, que finalmente se ejecuta por la JVM.

Si encuentra la inicialización de otras clases al inicializar la clase donde se encuentra el método principal, cargue primero la clase correspondiente y regrese después de que se complete la carga. Repetidamente bucles como este, y finalmente volver a la clase de método principal.

Cargador de clase

Clasificación

  • Iniciar el cargador de clases: Bootstrap ClassLoader, que se utiliza para cargar la biblioteca principal de Java, principalmente las clases en <JAVA_HOME> \ lib. Los programas Java no pueden hacer referencia directa a este cargador.

  • Cargador de clases de extensión: ExtClassLoader, que se utiliza para cargar clases de extensión de Java, principalmente en <JAVA_HOME> \ lib \ ext.

  • Cargador de clase de aplicación: AppClassLoader, que es responsable de cargar la clase especificada por la ruta de clase de usuario, los desarrolladores pueden usar directamente el cargador de clase, si no hay un cargador de clase personalizado en la aplicación, esta es la carga de clase predeterminada en el programa Dispositivo.

Modelo de delegación principal

Cuando se trata de cargadores de clases, se debe mencionar el modelo de delegación principal. El modelo de delegación principal significa que cuando llamamos al método loadClass del cargador de clases para cargar la clase, el cargador de clases primero solicitará que se cargue su cargador de clases principal y luego recursivamente. Si todos los cargadores de clases principales no se cargan, el cargador de clases actual realiza la operación de carga. Hacerlo puede garantizar la seguridad de las clases principales de Java, por ejemplo, no puede sobrescribir la clase java.lang.String.

Echemos un vistazo al modelo de delegación principal del código fuente de loadClass

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
        // 首先检查这个类是不是已经被加载了
        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) {
                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;
    }
}
复制代码

Cargador de clases personalizado

Si el cargador de clases personalizado no destruye el modelo de delegación principal, solo necesita heredar la clase ClassLoader y anular el método findClass.

Si el cargador de clases personalizado desea destruir el modelo de delegación principal, debe heredar la clase ClassLoader y anular el método loadClass y el método findClass.

Cuando se trata de destruir el modelo de delegación parental, es posible que se le pregunte si conoce algún ejemplo de cómo romper el modelo de delegación parental y cómo.

  • Java SPI necesita cargar las clases de proveedores externos (como las bases de datos de uso común) durante la carga de la biblioteca de clases principales rt.jar, y especificar directamente el cargador de clases de la aplicación para cargar estas clases.
  • El cargador de clases de contenedor web en Tomcat también destruye el modelo de delegación parental.Además de la biblioteca de clases principal, el WebApplicationClassLoader personalizado carga primero la Clase en su propia ruta

Supongo que te gusta

Origin juejin.im/post/5e9c330ae51d4546eb5257de
Recomendado
Clasificación