I. Introducción
Este es el JVM
quinto en el blog de la serie, y es el último, terminar este blog, voy a la parada temporal JVM
de aprendizaje, empecé a estudiar otros aspectos de la. Este blog sólo para hablar de JVM
cargadores de clases, así como el modelo de delegación cargador de clases padre.
En segundo lugar, el texto
2.1 ¿Cuál es el cargador de clases
En primer lugar tenemos que saber una cosa, y eso es lo que es el cargador de clases? Todos sabemos que nuestras necesidades de código para ser compilados a class
código de bytes a ser ejecutado, JVM
el intérprete sólo puede reconocer el código de bytes no se puede ejecutar Java
el código fuente. Los procedimientos se realizan en la memoria, por lo que ser capaz de ejecutar código de bytes, que tendrá que ser leído en la memoria. El código de bytes se leen en la memoria de trabajo, un cargador de clases se lleva a cabo. El cargador de clases proporcionó nombre completo de clase (nombre de paquete + nombre de la clase), la clase a encontrar el camino correspondiente class
archivo que fue leído a JVM
la zona método administrado, con el fin de ejecutar instrucciones de código en el mismo, la clase visitada datos. Sobre mecanismo de carga de clases, puede hacer referencia a este blog: la base JVM - clase de análisis de proceso de carga .
Para el cargador de clases, es necesario prestar atención a un problema. Cada clase se determina con carácter único y un mismo cargador de clases. ¿Qué significa esto? Es decir, si el código de bytes de una clase, utilizando dos cargador de clases diferentes para cargar, para la JVM, la carga será identificado como estas dos dos clases diferentes . Por ejemplo, tenemos una clase Test
, el cargador de clases crea una costumbre para cargarlo, y el cargador de clases personalizadas obtuvimos Class
crear objetos Test
objetos t
(reflexión), funcionando en el momento t instanceof Test
, obtenida será false
. Debido a que este objetivo se consigue mediante una clase personalizada El cargador carga la Test
creación, y la intanceof
declaración Test
es una JVM
clase cargada por Test
, por JVM
, esto es de dos clases diferentes. Además, para la clase tienen el mismo nombre completamente calificado del mismo cargador de clases va a cargar la carga sólo una vez, no se repite .
2.2 Clasificación cargador de clases
En Java
, el cargador de clases se divide generalmente en cuatro categorías, a saber:
- cargador de clases de arranque (en arranque cargador de clases);
- cargador de clases de extensión (Extensión ClassLoader);
- cargador de clases de aplicaciones (Aplicación ClassLoader);
- cargador de clases personalizado (cargador de clases de usuario);
He aquí un resumen de lo que cada uno de estos cuatro cargador de clases diferentes.
(1) Inicio de clases cargador (cargador de clases Bootstrap)
Inicio cargador de clases no es el Java
lenguaje de implementación, sino por la C++
realización de la (máquina virtual HotSpot), que se encarga de cargar %JAVA_HOME%\lib
directorio de clases, por ejemplo String
, Integer
, HashMap
que se colocan en esta categoría ....... directorio, por lo se carga por el cargador de clases de arranque. Además, JVM
también proporciona un parámetro de configuración -Xbootclasspath
, los archivos de clase (por ejemplo, bajo el directorio especificado por el jar
paquete, class
archivo) será cargado cargador de clases de arranque. Desde el cargador de clases se C++
implementa, que no pertenece a la Java
parte, pero una parte de la máquina virtual, no puede Java
directamente código de referencia. El código siguiente puede intentar obtener String
la clase del cargador de clases, es decir, el cargador de clases de arranque, pero el resultado es la producción null
, ya que no es Java
una parte de:
public static void main(String[] args) {
ClassLoader c = String.class.getClassLoader();
System.out.println(c); // 输出null
}
(2) cargador de clases extendida (Extensión ClassLoader)
Se cargador de clases de extensión Java
implementado, es la clase de implementación sun.misc.Launcher$ExtClassLoader
(el nombre puede verse que esta es una clase interna), este cargador de clases es responsable de cargar %JAVA_HOME%\lib\ext
el directorio de la clase. Dado que este es un cargador de clases Java
, se puede implementar directamente en el Java
programa de referencia. Se puede establecer un %JAVA_HOME%\lib\ext
tipo de directorio de Class
objetos para conseguir el cargador de clases, esto se puede obtener mediante el siguiente método (un modelo de delegación matriz con sede método, explicado más adelante):
public static void main(String[] args) {
// 获得自己写的类的加载器
ClassLoader c = Main.class.getClassLoader();
// Main加载器的父加载器就是扩展类加载器
ClassLoader c2 = c.getParent();
// 输出:sun.misc.Launcher$ExtClassLoader@eed1f14
System.out.println(c2);
}
(3) cargador de clases de la aplicación (Aplicación ClassLoader)
Este tipo de carga es el Java
lenguaje de implementación, la clase de aplicación es sun.misc.Launcher$AppClassLoader
de $
se puede ver ese símbolo, que es una clase interna. Es responsable de cargar la ruta de clase ( CLASSPATH
) en la biblioteca, y nuestro código también pertenecen (en el camino CLASSPATH
de la ruta actual si se incluye). Por lo tanto, cuando escribimos su propio Java
código no especifica el cargador de clases, este cargador se utiliza para cargar por defecto. Desde el cargador de clases se Java
implementa, puede ser referenciado en nuestro código, referencia de la siguiente manera:
public static void main(String[] args) {
// 方式1:使用自定义类的Class对象获得
ClassLoader c1 = Main.class.getClassLoader();
// 方式2:使用ClassLoader类的getSystemClassLoader方法获得
ClassLoader c2 = ClassLoader.getSystemClassLoader();
// 输出:sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(c1);
// 输出:true
System.out.println(c1 == c2);
}
(4) un cargador de clases personalizadas (cargador de clases de usuario)
Si es necesario, se puede escribir su propio cargador de clases, y he escrito cargador de clases que se llama un cargador personalizado. Escribir el cargador de clases personalizado es muy simple: heredada ClassLoader
clase, entonces anulan estos métodos pueden ser. Para la Java
realización del cargador de clases, que se invoca loadClass()
el método de carga de clases, por lo que podemos reemplazar este método, pero no se recomienda esta práctica, ya que es fácil de destruir el mecanismo de carga de clases (después de que los padres serán referidos al delegado modelo). Lo más recomendable es reemplazar el findClass()
método que va a loadClass()
ser llamado en. En el findClass()
método, la lectura a cargar la clase class
de archivo, se convierte en una matriz de bytes, y luego llamar a la clase principal defineClass
método para convertir una matriz de bytes Class
objeto se devuelve, como se muestra a continuación:
// 自定义类加载器,实现ClassLoader
class MyClassLoader extends ClassLoader {
//指定class文件的路径路径
private String path;
public MyClassLoader(String classPath) {
path = classPath;
}
/**
* 重写findClass方法
* @param name 是我们这个类的全路径
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class log = null;
// 获取该class文件字节码数组
byte[] classData = getData();
if (classData == null) {
throw new ClassNotFoundException("Class Not Found");
}
// 将class的字节码数组转换成Class类的实例,并返回
return defineClass(name, classData, 0, classData.length);;
}
/**
* 读取class,将字节码转换成字节数组
* @return
*/
private byte[] getData() {
// 省略读取的代码
// 普通IO读取即可
}
}
2.3 modelo de delegación de los padres
Relación entre la anterior correspondiente al modelo de delegación imagen se llama el cargador de clases padre. Para cada cargador de clases, cargador tiene un padre (excepto el cargador de clases de arranque). Cuando el cargador de clases para cargar una clase, será el primer delegado a su cargador de clases padre a la carga, si el padre también tiene un cargador cargador de padres, siguen encargado hasta el cargador de clases de arranque. Inicio intentos cargador de clases para cargar esta clase, si la clase en su propio directorio bajo gestión, y aún no se ha cargado, cargado con éxito, o dejado de carga; si no se pudo cargar, y luego al cargador de clases de extensión inferior a la carga, el cargador de clases de extensión realiza la misma operación. En general, el modelo de delegación de los padres es la primera clase a los padres intentos cargador de clases a la carga, si falla la carga, entonces se va a cargar por las subclases, cada capa es tan . En la implementación real, esta relación entre padres e hijos no se logra por la herencia, sino por la combinación lograda en ClassLoader
clase, hay una propiedad llamada parent
, es un puntero a sus referencias cargador padre (antes del código de cargador de clases de extensión se obtiene de esta manera).
Aquí estamos ClassLoader
tipo de loadClass
código fuente método, mirada en el código de implementación de modelo de delegación principal específico:
// name为类的全限定类名
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
// 这个重载方法被上面的方法调用
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// 对以下代码进行多线程同步
synchronized (getClassLoadingLock(name)) {
// 首先,判断这个类是否已经被加载
Class<?> c = findLoadedClass(name);
// c为null表示还没有被加载,则进行加载
if (c == null) {
long t0 = System.nanoTime();
try {
// 此处是双亲委托机制的关键
// 如果当前加载器有父加载器
if (parent != null) {
// 调用父加载器的loadClass对类进行加载
c = parent.loadClass(name, false);
} else {
// 若没有父类加载器,则调用引导类加载器尝试加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 此时加载失败将抛出异常,提示类找不到
// 不需要处理,继续执行
}
// c为null表示调用上层类加载器加载失败
if (c == null) {
long t1 = System.nanoTime();
// 调用当前类加载器的findClass进行加载
// 所以自定义类加载器推荐重写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;
}
}
Por el código anterior, podemos ver claramente cómo se implementa el mecanismo de los padres delegado. Que este mecanismo de qué sirve? Uso de padres delegación mecanismo asegura que la clase se carga condición satisface un primer cargador de clases. Para un cargador de clases para cada clase sólo será cargado una vez, por lo que la carga no hará que la misma clase varias veces, una situación para ser cargado o una clase de cargador de clases diferentes. Dicho antes, la única clase se determina por su propia y su cargador de clases a la carga, el modelo de delegación utilizando la clase de los padres es bueno para evitar la carga de una pluralidad de cargadores, el mismo no da como resultado equivalente en memoria clase. Por ejemplo, se define una java.lang.String
clase, si no hay un modelo de delegación de los padres, dos se produjeron en la memoria String
, pero en nuestra opinión, no sólo uno, en este momento hará que el programa para generar un inexplicable error. El modelo de delegación de los padres para asegurar que la clase del mismo nombre de esta manera no se carga a plazo, por lo que nuestra propia definición de la clase nunca será capaz de utilizar el mismo nombre, aunque el compilador puede (donde se puede probar su propia).
Por supuesto, los padres no se delegan modelo de JVM
especificación de mandato, sino una estrategia recomendada. Así que no podemos seguir este modelo al escribir su propio cargador de clases, tales como la reescritura loadClass
método cubiertos por este mecanismo. Pero si no es necesario, o no se recomienda.
En tercer lugar, el resumen
Y los padres sobre el cargador modelo de delegación de clase querrían decirlo. Las dos partes de la cara hizo una descripción bastante detallada, creo que sería después de leer Java
algunas de las características relevantes son más comprensión en profundidad, por lo general podemos resolver algunas dudas.
En cuarto lugar, la referencia
- "La comprensión en profundidad de la Máquina Virtual de Java"