Java虚拟机之类加载机制和原理附加双亲委派模型

版权声明:转载请注明出处!! https://blog.csdn.net/IPI715718/article/details/84660741

定义:虚拟机将class文件加载到内存,并对数据进行校验,准备,解剖,初始化后转化为直接使用的Java类型,就是类的加载机制。

类的加载过程

加载——》验证——》准备——》解剖——》初始化——》使用——》卸载。

类加载的时机

类在虚拟机中不是虚拟机启动的时候就将所有的class文件都进行加载,而是是动态加载的。

五种出发类加载的场景     当且仅当

  1. 当遇到new ,getstatic,setstatic,invokestatic命令时,并且类之前没有被加载过。
  2. 类被反射调用的时候,类之前没有被加载过。
  3. 当加载一个类时,发现他的父类没有被加载,那么就先加载他的父类。
  4. 入口方法所在的类,如main方法所在的类,当虚拟机启动时就会被加载。
  5. 当时用JDK1.7动态语言支持时,对一个实例的解剖结果是REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄,如果句柄所在的类未被加载过,就对这个类加载。

 加载

加载属于类加载的第一步

完成的三件事

  1. 根据全限定名,查找获取class文件。
  2. 将这个class文件的常量池中的数据转化为方法区常量池中的数据。
  3. 在内存中生成这个类的java.lang.Class对象,放入方法区。

 记载过程和验证的过程时交叉进行的,相辅相成,并且相互制约。

验证

扫描二维码关注公众号,回复: 5590818 查看本文章

验证虽然是类加载的第二步,但是在加类载的过程中的每一步就伴随着验证过程。

验证的阶段

  1. 文件格式的验证  这个阶段组要是围绕class文件进行的,会验证二进制字节码文件是否是class文件,是否符合虚拟机规范,只有经过着个验证通过之后,文件才会进入方法区。
  2. 元数据的验证   方法区数据结构阶段的验证,验证元数据信息的语义是否符合java的语义。
  3. 字节码验证  主要的目的是通过数据流和控制流的分析,确定语法是否合法,符合逻辑。
  4. 符号引用验证  在主要为了解剖阶段符号引用转化为直接引用时的验证  查看符号引用是否有相应的信息与之匹配。

准备 

准备阶段是为类变量(static修饰的变量)分配内存空间,并且赋初始值的阶段。这些变量都在方法区中分配空间。

 这里说的赋初值是赋数据类型的零值,例如 public static int value = 123;  ,这里赋值时velue赋值为0,因为int类型的零值是0。原因是因为此时为执行任何java代码,只是给类变量分配存储空间。注意:被static和final同时修饰的变量会在准备阶段赋制定初值,例如  public void static final int value = 123;  value就会赋值为123。

解剖

解剖阶段就是将符号引用转化为直接引用的阶段。

  • 直接引用:直接引用是直接指向对象的指针,字面值或者是指向能间接指向目标的句柄的指针,在内存中始终有一个目标与之匹配。存在于虚拟机中。
  • 符号引用:符号引用是用一组符号来描述所引用的目标,只起到描述引用目标的作用, 都存在与class文件中。

类加载器 

 Java团队将“通过类的全限定名来过去二进制class文件”交由虚拟机外部来实现,实现这个动作的模块就叫做类加载器。

对于任何一个类,都需要加载他的类加载器和类本身才能确定类在虚拟机的唯一性,也即是说同一个类被不同的类加载器加载,那么这两个类不相等。

双亲委派模型

从虚拟机的角度堆类加载器进行分类

1,启动类加载BootStrap ClassLoader  ,它是虚拟机内部的加载器,是C/C++语言编写的。

2,其他加载器,是虚拟机外部的加载器,由java语言开发, 其他加载器属于java里面的类,也是由BootStrap ClassLoader加载的。

从开发人员的角度对加载器进行分类

1,启动类加载器(BootStrap Classloader),随着java虚拟机的启动而启动,这个类加载器负责加载<JAVA_HOME>\lib下的类库,并且这些jar包必须是被虚拟机识别的类库(BootStrap Classloader加载器只对自己认识的文件名的jar包进行加载,不认识的名字的jar包即使放在lib文件夹中也不能被加载),BootStrap Classloader加载器不能直接被程序直接引用,因为不是java语言写的类。

2,扩展类加载器(Extension ClassLoader),由Java语言编写,也是一个类,这个加载器负责记载<JAVA_HOME>\lib\ext下的类库,开发者可以直接使用该加载器。

3,应用程序类加载器,由Java语言编写,也是一个类,负责加载用户ClassPath下的类,也是程序默认的加载器。

我们的程序运行中类的加载是由这几个加载器结合完成的,在加载器工作中的模型为:

双亲委派模型要求除了BootStrap ClassLoader 类加载器没有直接的父类加载器之外,都应该有自己的父类加载器,不是以继承关系工作,而是以组合的形式工作。

双亲委派模型工作流程

当一个类加载器接收到类加载请求的时候比如上图的User ClassLoader加载器,他不会直接去加载这个类,他会首先查看自己的缓存区域内有没有这个类,没有就向父类加载器Application ClassLoader委派,Application ClassLoader拿到请求之后也是先查看自己的缓存,如果没有就委派给他的父类加载器Extension Classloader,Extension ClassLoader查看自己的缓存,没有直接委派给启动加载器BootStrap Classloader,BootStrap Classloader加载器缓存中也没有的话就尝试去加载类,加载到就返回这个类的实例,加载不到,就交由子加载器加载,子类加载不到就依次传递给子类加载,最终交由自己来加载这个类,能加载到放入内存,加载不到就报java.lang.ClassNotFoundException。

委派是由下到上的,最顶级的类加载器如果加载不到,加载开始由上到下在家。这样能避免类的重复加载,类的唯一性得到了保证。

那么jvm是怎么判断一个类有没有已经加载过的呢?

当类加载器接到加载请求时,这里除了判断class文件是一致的之外,还需要判断是不是同一个类加载器加载的才行。比如App ClassLoader已经加载了一次Test类,当Test类再次加载请求时,除了要判断这次请求的Test类的Class文件与已经加载的Test类是否相同外,还要判断已经加载过的Test类的加载器和这次请求的类加载器是否相同。相同就是加载过,不相同就是没加载过,就开始采用双亲委派模型加载。

猜你喜欢

转载自blog.csdn.net/IPI715718/article/details/84660741