jvm系列-0X-双亲委派模型

957 阅读3分钟

java虚拟机规定,类加载遵循双亲委派模型。


如上图所示,存在2种类加载器:一种是引导类加载器。引导类加载器是用C++实现的,是java虚拟机自身的一部分,这也是区别于其他类加载器的地方。自然,剩下的类加载器都是由java实现的,独立于虚拟机外部,并且全都继承于java.lang.ClassLoader。

首先:将图中所列举的类加载器逐一详解:

由于classloader在jdk9开始就有很大不同,鄙人用的jdk11,所以用直接写成了jdk8和jdk11的区别了噢,但实际应该是jdk9就开始改了的 ,望周知~

jdk8:引导类加载器(Bootstrap ClassLoader):只加载核心api,具体负责加载放到<java_home>\lib目录中的;或者被-Xbootclasspath参数指定的路径中的,并且是虚拟机识别的,即不仅要路径符合,类名称也要符合。启动类加载器(就是引导类加载器)不能被java程序直接引用。

jdk11:引导类加载器(Bootstrap ClassLoader):bootstrap classloader加载lib/modules

注:如果获取一个类的classLoader,得到的是null,那说明该类的类装载器为Bootstrap ClassLoader. 如:

String.class.getClassLoder()的结果就是null.

jdk8:扩展类加载器(Extension ClassLoader):加载<java_home>/lib/ext,由sun.misc.Launcher$ExtClassLoader实现

jdk11:扩展类加载器(platform ClassLoader):ext classloader更名为platform classloader,加载lib/modules,类名变类,类的位置也变类,如下为jdk11的


jdk8:应用类加载器(application classloader):由于该类加载器是ClassLoader中的getSystemClassLoader()的返回值,因此也叫系统类加载器,由sun.misc.Launcher$AppClassLoader实现,继承于URLClassLoader,负责加载用户类路径(ClassPath)上指定的类库。即加载 -cp指定的类。一般开发者自定义的类,都是该类加载器加载的。

jdk11:同扩展类加载器,app ClassLoader在jdk11中的位置也由Launcher迁移到了ClassLoader里,并且不再继承于URLClassLoader.加载加载-cp,-mp指定的类。

public class LoadClassTest1 {    public  static void main(String[] strings){        LoadClassTest1 loadClassTest1=new LoadClassTest1();        System.out.println(loadClassTest1.getClass().getClassLoader());    }}

如图上,运行结果为:

jdk.internal.loader.ClassLoaders$AppClassLoader@77556fd


太用不惯掘金的这个文档编辑了啊啊啊啊啊

----------------------------------------------------------------------------------------

好了,介绍完各个类加载器,下面开始本文的重点内容:双亲委派模型。

系统中的ClassLoader在协同工作时,默认会使用双亲委派模型。即在类加载时,会判断当前类时否已经被加载,若已经加载,则直接返回可用的类,否则会将该加载请求委派给父类加载器器(该处的父类,并非是java里的继承关系)去完成,每一个层次的类加载器都是如此,直到最顶层的启动类加载器,只有当父类加载器无法加载时,才会向下由子类加载器去加载。

双亲委派模型好处:

1.避免类的重复加载

2.保护程序安全,防止核心api被随意篡改。

如:

我自己在java.lang下定义一个String类,看看会怎样:

package java.lang;
public class String {   
 static{        
        System.out.println("我是自定义String类的静态代码块");    
}
}

再通过一个类调用,如果加载的是自定义的String,那会打印上面的内容

package virtual;
public class LoadClassTest1 {    
public  static void main(String[] strings){ 
       String str=new String();
       System.out.println("load test")  
}
}

运行结果为:


即会直接报错。

如果在jdk8中 ,应该不会报错,而且加载的是系统中的String,而非自定义的string.