深入理解Java虚拟机第七章
一、类加载的生命周期(加载过程)
- 加载
a)通过一个类的全限定名来获取定义此类的二进制字节流。
b) 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
c) 在内存中生成一个代表这个类的java.lang.Class对象, 作为方法区这个类的各种数据的访问入口。- 验证:这一阶段用于确保Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身的安全。验证阶段大致分为:文件格式验证、元数据验证、字节码验证、符号引用验证。
- 准备:为类中定义的变量(静态变量、static修饰的变量)分配内存并设置类变量初始值阶段。
- 解析: 解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程。
- 初始化: 执行类构造器方法的过程。
注:假设一个类变量的定义为 public static int value = 6;
那么在准备阶段首先这时候进行内存分配的仅包括类变量,不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。也就是说变量value在准备阶段后的初始值是0,而把value赋值为123的putstatic指令是程序被编译后,存放于类构造器()方法之中,所以只有在类初始化阶段value的值才是123.
二、类加载器
2.1、类与类加载器
- 类加载阶段中“通过一个类的全限定名来获取描述该类的二进制字节流”,这个过程在JVM外部实现,以便让应用程序自己决定如何去获取所需的类,实现这个动作的代码被称为“类加载器”。
类加载器:通过类的全限定名来获取该类的二进制流的代码块就是类加载器。
注:不同类加载器所加载的同一个全限定名的类不相等,即Class对象的equals()方法,isInstance(),以及instanceof关键字,例子如下:
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
//自定义类加载器
ClassLoader myLoader= new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if (null == is){
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name,b,0,b.length);
} catch (IOException e) {
e.printStackTrace();
throw new ClassNotFoundException(name);
}
}
};
//通过自定义加载器获取Class对象
Object obj = myLoader.loadClass("lin.proxy.ProxyTest").newInstance();
System.out.println(obj.getClass());
//可以得出输出结果为false,即不同加载器加载同一个全限类名得到的类不是同一个对象
System.out.println(obj instanceof lin.proxy.ProxyTest);
}
}
- JVM两种不同的类加载器
a. 启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分。
b. 其他类加载器,由JAVA语言实现,独立于虚拟机外部,并且全部继承自抽象类java.lang.ClassLoader,分别为:扩展类加载器、应用程序类加载器、自定义类加载器。
2.2、双亲委派模型
各个加载器的作用:
- 启动类加载器(Bootstrap ClassLoader): 加载存放<JAVA_HOME>\lib目录,或者被-Xbootclasspath参数指定的路径中存放的,而且是JVM能够识别的(按照文件名识别,如rt.jar、tools.jar,名字不符合的类库即时放在lib目录中也不会被加载)类库加载到虚拟机的内存中。
- 扩展类加载器(Extension Class Loader):它复制加载<JAVA_HOME>\lib\et目录,或者被java.ext.dirs系统变量所指定的路径中的所有的类库。
- 应用程序类加载器(Application Class Loader): 由于应用程序类加载器是ClassLoader类中的getSystemClassLoader()方法的返回值,所以有些场景也称为“系统类加载器”。它负责加载用户类路径(ClassPath)上所有的类库,开发者可以直接在代码中使用这个类加载器。
- 双亲委派模型: 即各种类加载器之间的层次关系。
- 双亲委派模型工作机制:如果一个类加载器收到了类加载的请求,首先把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去完成加载。