类加载
java类加载器
Java中的类加载器,有启动类加载器(Bootstrap Classloader)、扩展类加载器(Launcher(ExtClassLoader)、应用程序类加载器(Launcher)AppClassLoader),用户还可以实现自定义的类加载器。
-
Bootstrap类加载器负责加载rt.jar中的JDK类文件,它是所有类加载器的父加载器。默认是负责加载$JRE_HOME/lib目录下面的类,也可以通过JVM参数-Xbootclasspath来指定需要加载类的路径.
-
Extension将加载类的请求先委托给它的父加载器,也就是Bootstrap,如果没有成功加载的话,再从jre/lib/ext目录下或者java.ext.dirs系统属性定义的目录下加载类。Extension加载器由sun.misc.Launcher$ExtClassLoader实现。
-
Application类加载器, 它负责从classpath环境变量中加载某些应用相关的类,classpath环境变量通常由-classpath或-cp命令行选项来定义,或者是JAR中的Manifest的classpath属性。Application类加载器是Extension类加载器的子加载器。通过sun.misc.Launcher$AppClassLoader实现。
Bootstrap类加载器是由C来写的,其他的类加载器都是通过java.lang.ClassLoader来实现的。
jvm启动类加载器加载过程:
- 根据JVM内存配置要求,为JVM申请特定大小的内存空间;
- 创建一个引导类加载器(Bootstrap Classloader)实例,初步加载系统类到内存方法区区域中;
- 创建JVM 启动器实例 Launcher,并取得类加载器ClassLoader(在Launcher的内部,其定义了两个类加载器,分别是sun.misc.Launcher.ExtClassLoader和sun.misc.Launcher.AppClassLoader);
- 使用上述获取的ClassLoader实例加载我们定义的Main类;
- 加载完成时候JVM会执行Main类的main方法入口,执行Main类的main方法;
- 结束,java程序运行结束,JVM销毁。
ClassLoader源码解析
1. loadClass
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); // 检查该类是否已经被JVM加载过,若加载过则直接返回
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;
}
}
由loadClass源码可知, loadCLass加载步骤为:
- 先从jvm中找, 若之前已经被加载则返回对应Class, 若为之前未被加载则走2
- 若此类加载器存在父类, 则交给父类加载, 若父类加载成功返回类对象, 加载失败返回null则走4
- 若此类加载器不存在父类, 则交给Bootstrap Classloader加载, 若加载成功返回类对象, 加载失败返回null则走4
- 上述加载失败, 调用当前类加载器的findClass加载, 加载成功返回类对象(加载失败后...).
由loadClass的逻辑已经能够清楚的看出双亲委派原则的实现逻辑:
首先都是交给父类去加载,如果父类无法加载再交给子类去完成,直到调用用户自定义的类加载器去加载,如果全部都无法加载,就会抛出ClassNotFoundException。
2. findClass
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
CLassLoader中的findClass交由子类实现。
3. defineClass
通过字节码byte[] 获取字节码对象。
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError {
return defineClass(name, b, off, len, null);
}
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError{
protectionDomain = preDefineClass(name, protectionDomain);
Class c = null;
String source = defineClassSourceLocation(protectionDomain);
try {
c = defineClass1(name, b, off, len, protectionDomain, source);
} catch (ClassFormatError cfe) {
c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,
source);
}
postDefineClass(c, protectionDomain);
return c;
}
参考:
JVM类加载器机制与类加载过程