类的加载流程
加载:
连接:
初始化:
双亲委派机制:
- 根加载器
- 拓展类加载器
- 应用类加载器
目的就是为了防止类的重复加载
那么这个类加载是否是线程安全的呢?
这个就要看下源码,一探究竟了
类加载器是负责加载类的对象。 ClassLoader 类是一个抽象类。给定类的二进制名称,类加载器应该尝试定位或生成构成类定义的数据。一种典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件”。
每个Class对象都包含对定义它的ClassLoader的reference 。
数组类的类对象不是由类加载器创建的,而是根据 Java 运行时的要求自动创建的。
Class.getClassLoader()返回的数组类的类加载器与其元素类型的类加载器相同;如果元素类型是原始类型,则数组类没有类加载器。
应用程序实现ClassLoader的子类以扩展 Java 虚拟机动态加载类的方式。
安全管理器通常可以使用类加载器来指示安全域。
ClassLoader类使用委托模型来搜索类和资源。 ClassLoader的每个实例都有一个关联的父类加载器。当请求查找类或资源时, ClassLoader实例将在尝试查找类或资源本身之前将对该类或资源的搜索委托给其父类加载器。虚拟机的内置类加载器,称为“引导类加载器”,它本身没有父级,但可以作为ClassLoader实例的父级。
支持并发加载类的类加载器被称为具有并行能力的类加载器,并且需要在类初始化时通过调用ClassLoader.registerAsParallelCapable方法来注册自己。请注意, ClassLoader类默认注册为具有并行功能。但是,如果它们具有并行能力,它的子类仍然需要注册自己。 在委托模型不是严格分层的环境中,类加载器需要具有并行能力,否则类加载会导致死锁,因为加载器锁在类加载过程中被持有(参见loadClass方法)。
通常,Java 虚拟机以平台相关的方式从本地文件系统加载类。例如,在 UNIX 系统上,虚拟机从CLASSPATH环境变量定义的目录中加载类。
下面看下 classLoader 最关键的部分,loadClass 的代码逻辑:
注意两个关键字:
protected 提供给子类覆盖自己的方法
synchronized 创建 Java 类过程中是线程安全的
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
// 注意,这里加了锁,说明加载流程是线程安全的
synchronized (getClassLoadingLock(name)) {
// 如果已经加载过来,则返回该 clss,否则返回 null
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// 向上委托类加载
if (parent != null) {
c = parent.loadClass(name, false);
} else {
// 根加载器加载
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();
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;
}
}