Java类加载器(classloader)的功能就是加载类的描述对象Class到内存中,可以加载本地的.class文件也可以加载网络上的数据,本质上做的事情就是由java字节码到java.lang.Class对象的过程。
类加载器一般情况下有三种:
第一种:Bootstrap ClassLoader。它 负责加载java基础类,主要是 %JRE_HOME/lib/ 目录下(或者通过参数-Xbootclasspath指定)的rt.jar、resources.jar、charsets.jar和class等。
第二种:Extension ClassLoader。 负责加载java扩展类,主要是 %JRE_HOME/lib/ext 目录下的jar和class(或者通过参数-Djava.ext.dirs指定)。
第三种:App ClassLoader(也叫System ClassLoader)。AppClassLoader会加载java环境变量CLASSPATH所指定的路径下的类库,而CLASSPATH所指定的路径可以通过System.getProperty("java.class.path")获取;当然,该变量也可以覆盖,可以使用参数-cp,例如:java -cp 路径 (可以指定要执行的class目录)。
对于App ClassLoader与Extension ClassLoader都是sun.misc.Launcher里的一个内部类,但是BootStrapClassLoader是一个由C++编写的类,涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不能直接通过引用进行操作。
首先我么看一下作为App ClassLoader与Extension ClassLoader基类是如何实例化的。
public Launcher() {
Launcher.ExtClassLoader var1;
try {
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
if(var2 != null) {
SecurityManager var3 = null;
if(!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
;
} catch (InstantiationException var6) {
;
} catch (ClassNotFoundException var7) {
;
} catch (ClassCastException var8) {
;
}
} else {
var3 = new SecurityManager();
}
if(var3 == null) {
throw new InternalError("Could not create SecurityManager: " + var2);
}
System.setSecurityManager(var3);
}
}
我们来解释一下这个这个过程都做了什么
- Launcher.ExtClassLoader.getExtClassLoader()获取ExtClassLoader,里面做的事情也就是初始化一个Launcher.ExtClassLoader的实例。new Launcher.ExtClassLoader(var0)其中这个var0就是ExtClassLoader要处理的路径中的文件。
- Launcher.AppClassLoader.getAppClassLoader(var1),其中这个var1,就是上面提到的ExtClassLoader,这个方法所做的事情,就是根据传入的ExtClassLoader以及AppClassLoader要处理的路径中的File
- 设置当前线程的classload为AppClassLoader。
- 如果应用设置了系统参数--安全管理器,会调用System.setSecurityManager设置进去。
既然我们谈到了类加载器就不得不说他是如何加载的。
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处理加载请求,递归了
c = parent.loadClass(name, false);
} else {
//没有parent说明是bootstrap
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;
}
}
ClassLoader中的双亲委派机制:
-
当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
-
当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
-
如果BootStrapClassLoader加载失败会使用ExtClassLoader来尝试加载;
-
若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。
双亲委派的好处:安全,因为ClassLoader加载的class文件来源很多,比如编译器编译生成的class、或者网络下载的字节码,而一些来源的class文件是不可靠的,比如说手动创建了一个java.lang.Integer 的类与jdk自带的相冲突,利用双亲委派的机制,逐级委托,最终会被bootstrapClassLoader所加载,加载的是jdk自带,不会加载自定义的,这样就保证了安全性。
网友评论