类加载子系统(类加载机制)
类加载器(classloader)的作用
加载 .class 文件(平台无关的二进制字节码文件)
classloader 有两种装载class的方式 (时机)
隐式:运行过程中,碰到new方式生成对象时,隐式调用classLoader到JVM
显式:通过class.forname()动态加载
装载器把一个类装入JVM中要要经过三个步骤来完成
1. 装载:查找和装入类或接口的二进制数据。
2. 连接:执行以下三步,其中解析是可选的
(1) 验证:检验装入类或接口的二进制数据的正确性。
(2) 准备:为静态变量分配存储空间。
(3) 解析:将常量池内的符号引用替换为直接引用。
3. 初始化:激活类的静态变量和静态Java代码块。
类加载器 classloader 是具有层次结构的,也就是父子关系。其中,Bootstrap 是所有类加载器的父亲。如下图所示:
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分工自治生态系统的一个很好的体现。
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.
声明:图片来自源于网络。这是之前整理的word笔记,没有注明图片具体来源,抱歉。在此向贡献图片的人表示感谢。