类加载机制
虚拟机把描述类的数据从Classs文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
类加载过程
加载器把一个类装入JVM中需要经过三个步骤完成:
- 加载:查找和装入类或接口的二进制数据
- 连接:执行以下三步,其中解析是可选的
(1)验证:检验装入类或接口的二进制数据的正确性
(2)准备:为静态变量分配存储空间
(3)解析:将常量池内的符号引用替换为直接引用
3.初始化:激活类的静态变量和静态java代码块
类加载器
类加载器classloader具有父子关系,如图:
Bootstrap class loader: 父类 当运行 java 虚拟机时,这个类加载器被创建,它负责加载虚拟机的核心类库,如 java.lang. 等。例如 java.lang.Object 就是由根类加载器加载的。需要注意的是,这个类加载器不是用 java 语言写的,而是用 CC++ 写的。
Extension class loader :这个加载器加载出了基本 API 之外的一些拓展类。
AppClass Loader :加载应用程序和程序员自定义的类。
除了以上虚拟机自带的加载器以外,用户还可以定制自己的类加载器(User-defined Class Loader)。Java 提供了抽象类 java.lang.ClassLoader,所有用户自定义的类加载器应该继承 ClassLoader 类。
这是JVM分工自治生态系统的一个很好的体现。
双亲委派模型(Parent Delegation Model)
JVM内置了三个默认的装载器
BootstrapLoader
ExtClassLoader
AppClassLoader
BootstrapLoader是由C/C++实现,我们无法在程序中获取它的实例,这个装载器负责装载lib目录下的dt.jar、tools.jar等Java核心核心类库。
ExtClassLoader这个装载器负责装载jdk/lib/ext目录下的jar包。
AppClassLoader这个装载器主要负责装载classpath目录下的类。
这三个装载器存在层级关系是,
BootstrapLoader为ExtClassLoader的父装载器,
ExtClassloader为AppClassLoader的父装载器。
类的装载遵循“双亲委派”模式,如果AppClassLoader被请求装载一个类,它首先会去询问ExtClassLoader是否已经装载,如果已经装载,则返回其对象;如果尚未装载,会继续询问BootstrapLoader,也就是说BootstrapLoader拥有最高的优先级。
类的加载过程采用双亲委托机制,这种机制能更好的保证 Java 平台的安全。该模型要求除了顶层的Bootstrap class loader启动类加载器外,其余的类加载器都应当有自己的父类加载器。
子类加载器和父类加载器不是以继承(Inheritance)的关系来实现,而是通过组合(Composition)关系来复用父加载器的代码。
每个类加载器都有自己的命名空间(由该加载器及所有父类加载器所加载的类组成,在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类;在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类)。
双亲委派模型的工作过程为:
1.当前 ClassLoader 首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。
每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存, 等下次加载的时候就可以直接返回了。
2.当前 classLoader 的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到 bootstrap ClassLoader。
3.当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。
使用这种模型来组织类加载器之间的关系的好处,主要是为了安全性,避免用户自己编写的类动态替换 Java 的一些核心类,比如 String,同时也避免了重复加载,因为 JVM 中区分不同类,不仅仅是根据类名,相同的 class 文件被不同的 ClassLoader 加载就是不同的两个类,如果相互转型的话会抛java.lang.ClassCaseException.
参考资料:https://blog.csdn.net/liyazhou0215/article/details/76793129