类加载
当jvm启动时会出现实话根加载器,根加载器会初始化Launcher类,在Launcher类中会初始化两个类加载器,一个是AppClassLoader另一个是ExtClassLoader,它们都继承于URLClassLoader,AppClassLoader中继承了顶级父类ClassLoader的parent属性,并指向ExtClassLoader类加载器引用,当执行new、getstatic、putstatic、invokestatic等指令以及反射等情况时会将没有初始化的类进行初始化,执行ClassLoader类中的loadClass方法,首先以目标加载的对象作为锁对象,然后判断类是否被加载过,如果没有被加载过就通过父加载器去加载,调用父加载器的loadClass方法也是调用同样的方法,同样父加载器也会判断是否已经加载过这个类了,如果没有加载就再次交给父加载器去进行加载,如果父加载器无法加载,最终会逐层通过自己的类加载器去加载这个类。
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
//利用要创建的对象作为锁
synchronized (getClassLoadingLock(name)) {
// 查看这个类是否被加载过
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//如果没有被记载过,那就交给父加载器去加载
c = parent.loadClass(name, false);
} else {
//如果parent为空说明当前的类加载器是extClassLoader,extClassLoader的parent是null,根加载器是C语言实现,指向为空,直接调用C提供的方法去查询根加载器是否加载过或跟加载器去加载
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 = java.net.URLClassLoader.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、沙箱安全机制,java的核心类比如rt.jar、ext包下的类,都将由根加载器以及ExtClassLoader去加载,保证java的核心类不会被篡改。
类加载流程
1、加载
将.class文件加载到内存之中存入元数据区,并在堆内存中创建相应的Class对象,这个对象指向元数据区该对象的Class类元信息。
2、连接
2.1、校验
检查.class的格式以及语法是否合法。
2.2、准备
准备阶段是将为类中的静态变量划分内存空间并赋予初始值。
2.3、解析
将.class文件中都有一个常量池的区域,这个区域也被称之为静态常量池,这个常量池中存储的都是符号引用等信息,将calss文件加载到内存后就变成了运行时常量池,就需要把符号引用转变为直接引用,这个符号引用就是某一个方法名,例如test(),直接引用就是这个test()在内存中的地址值。
3、初始化
这个阶段是一个执行构造方法的一个阶段,这个阶段首先会去判断父类有没有初始化,如果父类没有初始化,就会先执行父类的初始化,具体顺序如下:
1、执行父类的静态变量的赋值、静态代码块。
2、执行子类的静态变量的赋值以及静态代码块的执行。
3、执行父类的成员变量以及实例初始化代码块。
4、执行父类的构造方法。
5、执行子类的成员变量与实例初始化代码块。
6、执行子类的构造方法。