今天在网上收集了classLoader的学习,综合了各个的博客,在此记录,
一、类加载结构
1、bootstrap classloader 启动类加载器
(1) 运行虚拟机时候,这个类加载器被加载
(2)加载java 基本api,负责jdk_home/lib目录下的核心 api 或者-X bootclasspath 选项指定的jar包装入工作
(3)不是java语言实现的
2、extension classloader 扩展类加载器
(1)主要负责jdk_home/lib/ext目录下的jar包或 -D java.ext.dirs 指定目录下的jar包装入工作
3、system classloader 系统类加载器
(1)主要负责java -classpath或者-Djava.class.path或者CLASSPATH操作系统属性所指定的JAR类包 所指的目录下的类与jar包装入工作
4、user custom classloader 用户自定义类加载器
(1)在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性.
类机构:
二、类加载器的特性
1、每个ClassLoader都维护了一份自己的名称空间, 同一个名称空间里不能出现两个同名的类
这么说,我们是否可以定义一个类String?答案是可以的
2、为了实现java安全沙箱模型顶层的类加载器安全机制, java默认采用了 ” 双亲委派的加载链 ” 结构.
三、Launcher
(1)Launcher的理解
Launcher是JVM是为我们来建立类加载器的结构的一个重要对象。
sun.misc.Launcher ,顾名思义,当你执行java命令的时候,JVM会先使用bootstrap classloader载入并初始化一个Launcher,执行下来代码:
System.out.println("the Launcher's classloader is "+sun.misc.Launcher.getLauncher().getClass().getClassLoader());
结果为:
the Launcher's classloader is null (因为是用bootstrap classloader加载,所以class loader为null)
(2)Launcher的代码实现
public class Launcher {
public Launcher() {
ExtClassLoader extclassloader;
try {
//初始化extension classloader
extclassloader = ExtClassLoader.getExtClassLoader();
} catch(IOException ioexception) {
throw new InternalError("Could not create extension class loader");
}
try {
//初始化system classloader,parent是extension classloader
loader = AppClassLoader.getAppClassLoader(extclassloader);
} catch(IOException ioexception1) {
throw new InternalError("Could not create application class loader");
}
//将system classloader设置成当前线程的context classloader(将在后面加以介绍)
Thread.currentThread().setContextClassLoader(loader);
......
}
public ClassLoader getClassLoader() {
//返回system classloader
return loader;
}
}
显然,Launcher加载了extension classloader和system classloader
四、上下文类加载器
上述代码中setContextClassLoader 就是上下文类加载器,
使用线程上下文类加载器, 可以在执行线程中, 抛弃双亲委派加载链模式, 使用线程上下文里的类加载器加载类。
大部分java app服务器(jboss, tomcat..)也是采用contextClassLoader来处理web服务。
五、加载class的两种方式
(1)ClassLoader.loadClass(String name);
这种方式实现的是双亲委托方式,
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
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) {
//递归判断
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;
}
}
从这上面可以看出,双亲委托方式的算法逻辑。
(2)Class.forName(String name);
public static Class<?> forName(String className)
throws ClassNotFoundException {
return forName0(className, true, ClassLoader.getCallerClassLoader());
}
/** Called after security checks have been made. */
private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException;
forName方式,使用的是当前的classloader,最后是一个native方法