java虚拟机基础知识大全 java虚拟机运行原理( 五 )


从java虚拟机角度看有俩种不同的类加载器:
一:启动类加载器( ) C++实现
二:所有其他的类加载器(全部都继承自抽象类java.lang.) java实现
从开发人员角度看:
启动类加载器
作用:负责将存放在lib目录中的,或者被-参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机中 。启动类加载器无法被java程序直接引用,用户在编写自定义类加载时,如果需要把加载请求委派给引导类加载器,那直接使用null代替即可,
扩展类加载器
这个加载器由sun.misc.$实现,它负责加载目录中的,或者被java.ext.dirs系统变量所指定的路径的所有类库,开发者可以直接使用扩展类加载器
应用程序加载器
这个类加载器是由sun.misc.$实现 。由于这个类加载器是中的()方法的返回值,所以一般也称它为系统类加载器 。它负责加载用户类路径()上所指定的类库,开发者可以直接使用这个类加载器 。如果应用程序中没有自定义自己的类加载器,一般情况下这个就是程序中默认的类加载器 。
双亲委派模型的工作流程:
当类加载器接收到类加载的请求时,它不会自己去尝试加载这个类,而是把这个请求委派给父加载器去完成,每一个层次的类加载器都是如此,因此所有的请求最终都应该传送到启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围内没有找到所需的类)时,子加载器才会尝试自己去加载 。
优点:java类随着它的类加载器一起具备了一种带有优先级的层次关系 。
举例:比如我们要加载java.lang.,它存放在rt.jar中,无论哪个类加载器要加载换个类,都会委派给处于模型最顶端的启动类加载器进行加载,因此类在程序的各种类加载器环境中都是同一个类(上面提到了如何比较俩个类是否'相等') 。相反,如果没有双亲委派模型,那么各个类加载器都去自行加载的话,那么在程序中就会出现多个类,导致应用程序一片混乱 。
双亲委派模型的实现
Class ( name,)on{
//首先检查请求的类是否已经被加载过
Class c = (name);
if(c == null){
try{
if( != null){
//委派父类加载器加载
c = .(name, false);
}
else{
//委派启动类加载器加载
c = Null(name);
}
}catch(on e){
//父类加载器无法完成类加载请求
}
if(c == null){
//本身类加载器进行类加载
c = (name);
}
}
if(){
(c);
}
c;
}
双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器 。不过这里类加载器之间的父子关系一般不是以继承()的关系来实现的,而是通常使用组合()关系来复用父加载器的代码 。
JDK12才有双亲委派模型,面对已经存在的用户自定义类加载器的代码,为了兼容这些已有代码,无法再以技术手段避免()被子类覆盖的可能性,只能在JDK 1.2之后的java.lang.中添加一个新的方法(),并引导用户编写的类加载逻辑时尽可能去重写这个方法,而不是在()中编写代码 。
由这个模型自身的缺陷导致的,如果有基础类型又要调用回用户的代码 。
由于用户对程序动态性的追求而导致的,这里所说的“动态性”指的是一些非常“热”门的名词:代码热替换(Hot Swap)、模块热部署()等
OSGi实现模块化热部署的关键是它自定义的类加载器机制的实现,每一个程序模块(OSGi中称为)都有一个自己的类加载器,当需要更换一个时,就把连同类加载器一起换掉以实现代码的热替换 。在OSGi环境下,类加载器不再双亲委派模型推荐的树状结构,而是进一步发展为更加复杂的网状结构