Mecanismo de carga de clase de reflexión JavaSE

Uno, mecanismo de carga de clases

  • Cuando ejecutamos el programa con comandos de Java, primero necesitamos cargar la clase en la JVM a través del ClassLoader (ClassLoader)
  • java.lang.ClassLoader: según el nombre de una clase especificada, busque o genere su código de bytes correspondiente y luego defina una clase Java a partir de estos códigos de bytes, es decir, una instancia de la clase java.lang.Class,java.lang.ClassLoader tiene dos métodos principales, loadClass (String, boolean) y findClass. El primero implementa el mecanismo de delegación padre, el último completa la búsqueda y carga de la clase. La implementación predeterminada de findClass es un método vacío.

En segundo lugar, el proceso de carga de clases

1. Carga

  • Ubique en el disco duro y lea el archivo de código de bytes (archivos .class) a través de E / S, los datos binarios del archivo de clase de código de bytes en la memoria , un área de datos en la que se encuentra el área de método de tiempo de ejecución ,Cargar cuando se usa la clase, Como llamar al método main () de la clase, el nuevo objeto, etc.,En la fase de carga, un objeto java.lang.Class que representa esta clase se generará en la memoria (en el montón) como la entrada de acceso para varios datos de esta clase en el área de métodos.Además, si la clase principal usa otras clases durante el proceso de ejecución, estas clases se cargarán gradualmente. Las clases en el paquete jar o el paquete war no se cargan todas a la vez, se cargan solo cuando se usan (En el código fuente del constructor de la clase Class, el constructor es privado y solo JVM puede crear objetos de esta clase.

2. Verificación

  • Verificar la exactitud de los archivos de código de bytes

3. Preparar

  • Asignar memoria a variables estáticas de la clase y asignar valores predeterminados, como int asigna 4 bytes y asigna el valor 0, long asigna 8 bytes y asigna el valor 0

4. Análisis

  • La referencia de símbolo describe el destino al que se hace referencia mediante un conjunto de símbolos. Los símbolos pueden ser literales en cualquier forma. La forma literal de la referencia de símbolo está claramente definida en el formato de archivo Class de la especificación de la máquina virtual Java. En el momento de la compilación, la clase Java no conocer La dirección real de la clase referenciada, por lo que las referencias simbólicas solo se pueden usar en su lugar
  • Cita directa:
  1. Puntero directamente al objetivo
  2. Desplazamiento relativo
  3. Un mango que puede localizar indirectamente el objetivo.
  • Reemplazar referencias de símbolos con referencias directas, es decir, JVM reemplaza las referencias de símbolos en el grupo constante con referencias directas . El análisis es principalmente para 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. Se realiza la referencia de símbolo. Por ejemplo, un método en la clase A se refiere al método b en la clase B, luego encontrará la dirección de memoria del método b en la clase B y reemplazará la referencia del símbolo con una referencia directa (dirección de memoria), que es el llamado proceso de vinculación estática (completado durante la carga de la clase), la vinculación dinámica se realiza durante la ejecución del programa, reemplazando las referencias de símbolos con referencias directas

5. Inicialización

  • Inicialice las variables estáticas de la clase al valor especificado y ejecute el bloque de código estático

5.1 Proceso de inicialización del cargador de clases

  • La instancia del lanzador de JVM sun.misc.Launcher se crea durante la ejecución y carga de la clase. La inicialización de sun.misc.Launcher utiliza un patrón de diseño singleton para garantizar que solo haya una instancia de sun.misc.Launcher en una máquina virtual de JVM, que está dentro del método de construcción de Launcher., Creó dos cargadores de clases, a saber, sun.misc.Launcher.ExtClassLoader (cargador de clases extendido) y sun.misc.Launcher.AppClassLoader (cargador de clases de aplicación), JVM utiliza por defecto el método getClassLoader () de Launcher para return Una instancia del cargador de clases AppClassLoader carga la aplicación

5.2 La clase no se inicializa necesariamente después de cargarse en la memoria, lo que activa el método de inicialización activo del cargador de clases

  1. Cree una instancia del objeto, es decir, la inicialización de la clase se activa cuando se crea el nuevo objeto (siempre que la clase no esté inicializada)
  2. Llame a las propiedades estáticas de la clase o asigne valores a las propiedades estáticas
  3. Llame al método estático de la clase
  4. Crea objetos a través de la reflexión de archivos de clase
  5. Inicialice una subclase de una clase: primero inicialice la clase padre cuando use la subclase
  6. La clase que está marcada como la clase de inicio cuando se inicia la JVM (es decir, la clase donde se encuentra el método principal)
  • Las clases solo se pueden inicializar una vez en el mismo cargador de clases, y las que se han inicializado no necesitan inicializarse
  • Se puede determinar al compilar (constante de compilación), la clase no se inicializará
  • No se puede determinar en tiempo de compilación (ejecutar constante), inicializar la clase
  • Antes de inicializar la clase, se deben realizar los primeros 4 pasos (carga, verificación, preparación, análisis)
  • Si esta clase tiene una clase principal y la clase principal no se ha inicializado, inicialice primero la clase principal
  • Si hay instrucciones de inicialización en la clase, las instrucciones de inicialización se ejecutan a su vez.

5.3 Pasos de inicialización de clases

  • Los bloques de código estático y las propiedades estáticas se cargan al mismo nivel, es decir, se ejecutan en el orden en que se escribe el código.

5.3.1 Sin padre

  1. Propiedades estáticas de la clase
  2. Bloque de código estático
  3. Propiedades no estáticas de la clase
  4. Bloque de código no estático
  5. Método de construcción

5.3.2 Tiene un padre

  1. Propiedades estáticas de la clase padre
  2. El bloque de código estático de la clase padre
  3. Propiedades estáticas de subclases
  4. Bloque de código estático de subclase
  5. Propiedades no estáticas de la clase padre
  6. Bloque de código no estático de la clase padre
  7. Método de construcción de la clase principal
  8. Propiedades no estáticas de subclases
  9. Bloque de código no estático de subclase
  10. Método de construcción de subclase

En tercer lugar, el cargador de clases

  • El proceso de carga de clases se implementa principalmente a través de cargadores de clases. Hay varios cargadores de clases en Java. A cada cargador de clases se le ha asignado un directorio correspondiente cuando se crea. Es decir, se determina dónde va cada cargador de clases para cargar la clase. Además , la relación entre cargadores de clases no es una relación de herencia, sino una relación de delegación
  • Desde la perspectiva de la máquina virtual JVM, hay dos cargadores de clases diferentes, el cargador de clases de inicio y todos los demás cargadores de clases. El cargador de clases de inicio se implementa en C ++ y es parte de la propia JVM; todos los demás cargadores de clases se implementan en el lenguaje Java, independientemente de la máquina virtual, y todos heredan de java.lang.ClassLoader
  • Desde la perspectiva de los desarrolladores, los cargadores de clases se dividen en las siguientes categorías

1. cargador de clases de arranque

  • Bootstrap ClassLoader es el cargador de clases de arranque, que se encarga de cargar las bibliotecas de clases principales (jre \ lib \ rt.jar) en el directorio lib del JRE que admite la operación de JVM, como rt.jar, charsets.jar, etc.

2. Cargador de clases de extensión

  • Extension ClassLoader es el cargador de clases de extensión, que es responsable de cargar el paquete jar (jre \ lib \ ext * .jar) en el directorio de extensión ext bajo el directorio lib de JRE que admite la operación JVM

3. Cargador de clases de aplicaciones

  • Application ClassLoader es el cargador de clases de aplicaciones, responsable de cargar el paquete de clases en la ruta de ClassPath especificada, es decir, cargar la clase escrita por usted mismo

4. Cargador personalizado

  • User ClassLoader es un cargador personalizado, responsable de cargar paquetes de clases en rutas definidas por el usuario
  • La forma de implementar un cargador de clases personalizado: heredar la clase java.lang.ClassLoader y anular el método findClass ()

4. Mecanismo de delegación de los padres

  • Cuando se carga una determinada clase, primero le encargará al cargador principal que encuentre la clase de destino, y luego le confiará al cargador principal superior que cargue si no puede encontrarla. Si todo el cargador principal no puede encontrar la clase de destino en su propio cargando la ruta de clase, estarán en su propia clase Buscar y cargar la clase de destino en la ruta de carga

1. Razones para diseñar un mecanismo de delegación principal

  1. Mecanismo de seguridad sandbox: Por ejemplo, la clase java.lang.String.class escrita por usted mismo no se cargará, por lo que puede evitar que la biblioteca de API principal sea manipulada a voluntad;
  2. Evite la carga repetida de clases: Cuando el padre ya ha cargado la clase, no es necesario volver a cargar la subclase ClassLoader para garantizar la unicidad de la clase cargada

2. Analice el mecanismo de delegación principal en el método loadClass de ClassLoader

  1. Primero verifique si la clase con el nombre especificado se ha cargado, si se ha cargado, no es necesario cargarla, solo regrese
  2. Si no se ha cargado, determine el cargador principal, si el cargador principal del cargador actual no está vacío, entonces confíe al cargador principal para cargar la clase, si el cargador principal del cargador actual está vacío, confíe al cargador de clases de arranque para cargar el clase
  3. Si ni el cargador padre ni el cargador de clases de arranque encuentran la clase especificada, entonces llame a URLClassLoader, el método findClass del cargador de clases actual para buscar y cargar la clase en la ruta de clases del cargador.
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();
                    // 都会调用URLClassLoader即当前类加载器的findClass方法在加载器的类路径里查找并加载该类
                    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/LvJzzZ/article/details/108520132
Recomendado
Clasificación