JVM中类的生命周期包括7个阶段,加载、准备、验证、解析、初始化、使用、卸载。其中准备、验证、解析被归为连接阶段。
加载
jvm在这个阶段完成的工作
- 通过类名获取类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在堆中生成一个代表该类的
java.lang.class
对象,作为访问类在方法区中数据的入口
在这个阶段开发者可以控制二进制字节流的获取,也就是可以通过自定义的类加载器做自己定制化的操作。
验证
顾名思义,验证被加载的类的正确性。
- 文件格式验证:验证字节流是否符合Class文件格式的规范;例如:是否以 0xCAFEBABE开头
- 元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求
- 字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
- 符号引用验证:确保解析动作能正确执行
准备
为类的 静态变量分配内存,并将其初始化为默认值
- 只分配静态变量内存
- 初始化默认值是类型的默认值(即int:0、boolean:false...),不是代码显示设置的初始值
- 如果是
final static
修饰的变量则会赋值为代码中的初始值(即:final static int val=3,这时val赋值为3,而不是0)
解析
把类中的符号引用转换为直接引用
符号引用就是一组符号来描述目标(例如:ArrayList)。
直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。
初始化
类变量的初始化
- 定义类变量时初始化
- 静态代码块初始化
触发类初始化的场景
- 创建类实例,即new对象
- 访问静态变量
- 访问静态方法
- 反射调用(即Class.forName("com.xxx.Obj"),Obj类被初始化)
- 子类被初始化则父类被初始化
类加载器
- 启动类加载器(BootStrap ClassLoader):负责加载jre\lib下或者-Xbootclasspath 参数指定的路径下的能被jvm识别的类库。开发者无法直接使用
- 拓展类加载器(Extension ClassLoader):
sun.misc.Launcher$ExtClassLoader
,它负责加载 \jre\lib\ext目录中,或者由 java.ext.dirs系统变量指定的路径中的所有类库。开发者可以直接使用。 - 应用类加载器(Application ClassLoader):
sun.misc.Launcher$AppClassLoader
,它负责加载用户类路径(ClassPath)所指定的类。开发者可以直接使用 - 自定义类加载器(Custom ClassLoader):用户可以自定义类加载器
双亲委派模型
双亲委派模型的实现,当一个类加载器需要加载类时,会把这个任务委派给父级类加载器,依次向上,直到顶层启动类加载器为止,如果父级无法加载,再自己处理加载。
双亲委派模型的好处是,保证同一类环境中只有一个相同的类。也就是说JVM中判断是否是同一个类的条件是,是否相同的类加载器,类本身相同。
代码示例:
public class ClassLoaderTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
// 使用ClassLoaderTest的类加载器加载本类
Object obj1 = ClassLoaderTest.class.getClassLoader().loadClass("com.ognice.ClassLoaderTest").newInstance();
System.out.println(obj1.getClass().getClassLoader());
System.out.println(obj1 instanceof ClassLoaderTest);
// 使用自定义类加载器加载本类
ClassLoader customClassLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println("custom classloader loading " + name);
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream stream = getClass().getResourceAsStream(fileName);
if (stream == null) {
return super.loadClass(name);
}
try {
byte[] b = new byte[stream.available()];
stream.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
e.printStackTrace();
}
// 父级找class
return super.loadClass(name);
}
};
Object obj2 = customClassLoader.loadClass("com.ognice.ClassLoaderTest").newInstance();
System.out.println(obj2.getClass().getClassLoader());
System.out.println(obj2 instanceof ClassLoaderTest);
}
}
执行结果
sun.misc.Launcher$AppClassLoader@18b4aac2
true
custom classloader loading com.ognice.ClassLoaderTest
custom classloader loading java.lang.Object
custom classloader loading java.lang.ClassLoader
custom classloader loading com.ognice.ClassLoaderTest$1
com.ognice.ClassLoaderTest$1@277c0f21
false