JVM类加载机制
当我们运行某个类的main函数时,首先需要通过类加载器将主类加载到JVM中去。
类加载过程
加载
在硬盘上找到对应的.class文件,并且通过IO 读入
验证
校验字节码文件的正确性
准备
给类的静态变量分配内存,并赋予默认值
解析
将符号引用替换为直接引用,该阶段会把一些静态方法(静态方法,例如main()方法,替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换成直接引用)
符号引用&直接引用
在类的加载过程中的解析阶段,Java虚拟机会把类的二进制数据中的符号引用 替换为 直接引用,如Worker类中一个方法:
public void gotoWork(){
car.run(); //这段代码在Worker类中的二进制表示为符号引用
}
在Worker类的二进制数据中,包含了一个对Car类的run()方法的符号引用,它由run()方法的全名 和 相关描述符组成。在解析阶段,Java虚拟机会把这个符号引用替换为一个指针,该指针指向Car类的run()方法在方法区的内存位置,这个指针就是直接引用。
初始化
对类的静态变量初始化为指定的值,执行静态代码块
使用
卸载
类加载器
启动类加载器
是由C++实现的,这个类加载器负责放在<JAVA_HOME>\jre\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库。用户无法直接使用。
扩展类加载器
这个类加载器由sun.misc.Launcher$AppClassLoader实现。它负责<JAVA_HOME>\jre\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库。用户可以直接使用。
应用程序类加载器
这个类由sun.misc.Launcher$AppClassLoader实现。是ClassLoader中getSystemClassLoader()方法的返回值。它负责用户路径(ClassPath)所指定的类库。用户可以直接使用。如果用户没有自己定义类加载器,默认使用这个。
自定义加载器
用户自己定义的类加载器。
双亲委派机制
当需要加载一个类的时候首先是找到应用类加载器去加载,应用类加载器会向上委托给扩展类加载器,扩展类向上委托给启动类加载器,启动类加载器在核心类库中(<JAVA_HOME>\jre\lib)找不到该类时,会让扩展类自己加载,扩展类加载器(<JAVA_HOME>\jre\lib\ext)也没找到该类时就会让应用类加载器自己到(classpath)加载,这就是双亲委派机制。如下图示意:
接下来我们看看源码的实现,由于ExtClassLoader中没有覆写loadClass()方法,而AppClassLoader的loadClass()方法最终是指向ClassLoader类中的loadClass()这个方法,所以我们直接看ClassLoader中的代码
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) {
// 如果父类不为空,调用parent(该parent不是通过extend来的父类,而是在Launcher类中指定的)
// 的loadClass
c = parent.loadClass(name, false);
} else {
// 由于BootstrapClassLoader是C++实现的,所以ExtClassLoader的parent赋值是null
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.
// 如果上面的classLoader都没找到就自己找
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;
}
}
双亲委派机制的作用
1.防止核心类库被修改
2.避免类的重复加载