博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JVM类加载机制
阅读量:6495 次
发布时间:2019-06-24

本文共 3518 字,大约阅读时间需要 11 分钟。

1. 简介

Java代码编译成字节码后,字节码需要通过虚拟机的加载才能运行

2. 类加载过程

类加载的全过程分为5个阶段,其中验证、准备、解析属于连接阶段

  1. 加载
  2. 验证
  3. 准备
  4. 解析
  5. 初始化

加载

加载是类加载过程的一个阶段,此阶段可以分为三步

  1. 通过一个类的全限定名(如java.lang.Object)来获取此类的二进制流
  2. 将这个字节流所代表的静态数据结构方法区运行时的数据结构
  3. 在内存中生成代表这个类的Class对象(Java虚拟机规范并没有明确规定这个对象在Java堆里,但是对于HotSpot虚拟机而言,Class对象存在于方法区中)

这个阶段是由类加载器(ClassLoader)来完成

验证

验证是连接阶段的第一步,目的是确保Class文件的字节流中包含的信息符合当前虚拟机的要求。包括四个方面的验证:

  • 文件格式验证
  • 元数据验证
  • 字节码验证
  • 符号引用验证

准备

准备阶段时正式为类变量分配内存并设置类变量初始值的阶段,初始值一般是零值,不一般的情况就是类变量用 final修饰初始值是绑定的常量值。

解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。

初始化

  • 初始化阶段是执行类构造器()方法的过程,即静态语句块。
  • 虚拟机会保证父类的()方法先执行。而如果初始化的是接口,并不需要先执行父接口的()方法,只有在使用父接口定义的变量时才会执行。
  • 虚拟机会保证执行()方法时会正确加锁同步。

3. 类加载器

类加载的加载阶段是由类加载器来完成的。即使是同一个类,由不同的类加载器来加载,两个类也是不相等的,体现在:

  • Class对象的equals()方法
  • Class对象的isAssignableFrom方法
  • Class对象的isInstance()方法
  • 使用instanceof关键字判断对象所属的类

类加载器的分类

对于虚拟机来说只存在两种不同的类加载器:

  1. 启动类加载器(Bootstrap ClassLoader)
    • 使用C++实现,属于虚拟机自身的一部分。
    • 这个类负责加载<JAVA_HOME>\lib目录下,或者被-Xbootclasspath参数指定的路径,并且能够被虚拟机识别的类
    • 启动类加载器无法直接被Java程序引用
  2. 其他类加载器。使用Java实现,独立于虚拟机外部。并且继承与抽象类java.lang.ClassLoader
    • 扩展类加载器(Extension ClassLoader)
      • 这个类加载器是由sun.misc.Launcher$ExtClassLoader实现
      • 负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量指定的路径的所有类库
      • 扩展类加载器可以直接使用
    • 应用程序类加载器(Application ClassLoader)
      • 这个类加载器是由sun.misc.Launcher$AppClassLoader实现
      • 负责加载用户类路径(ClassPath)上的类库
      • 应用程序类加载器可以直接使用,同时这个类加载器也是ClassLoader类的getSystemClassLoader()静态方法的返回值

类加载器的层次关系(双亲委派模型)

双亲委派模型

下图是类加载器之间的层次关系,也称为双亲委派模型(Parents Delegation Model)。可以看出除了启动类加载器之外,其他类加载器都有自己的父加载器,但这里的层次关系是以组合来实现,而不是继承(extends)。

工作方式

双亲委派模型的工作方式是:当一个类加载器收到加载类的请求时,它会先把这个加载请求委托给父类加载器,如果父类加载器不能完成加载(如在自己的搜索路径下找不到该类),这时再由自己来完成加载这个类。通过这个方法,加载请求会委托到启动加载器,如果启动类加载器完成不了再依次向下的类加载器加载。

作用

双亲委派模型的作用是所有的加载请求都最先由顶层的类加载器去完成,这样可以保证加载出来的类是同一个,不会混乱。 打个比方,我们自己实现了两个类加载器,而这个两个类加载器都不是按照双亲委派模型去实现的,而是自己去对应目录下加载类。现在我们让两个类加载器加载去加载java.lang.Object类,加载出来的两个类必定是不同的(不同的类加载器加载出来的类是不同的)。

自定义类加载器

public Class
loadClass(String name) throws ClassNotFoundException { return loadClass(name, false);}protected Class
loadClass(String name, boolean resolve) throws ClassNotFoundException{ synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class
c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; }}复制代码

上面的代码是抽象类ClassLoaderloadClass方法,通过类的全限定名来加载类,其中loadClass(String name, boolean resolve)方法实现了双亲委派法。具体步骤为:

  1. 通过findLoadedClass方法检查当前类加载器是否加载过该类,加载过就直接返回该Class对象。
  2. 如果没有加载过就将该加载请求委托给父加载器,如果没有父加载器则委托给启动类加载器。
  3. 如果父加载器无法完成请求则自己使用findClass方法加载该类

所以现在自定义类加载器想依照双亲委派模型去实现可以直接继承ClassLoader类并重写findClass方法。要是不想受照双亲委派模型约束可以直接重写loadClass方法。

转载地址:http://atuyo.baihongyu.com/

你可能感兴趣的文章
[linux基础学习]run level
查看>>
第七周学习总结
查看>>
一步步的教你安装UChome (UChome 安装教程)
查看>>
[DeeplearningAI笔记]序列模型1.5-1.6不同类型的循环神经网络/语言模型与序列生成...
查看>>
P2533 [AHOI2012]信号塔
查看>>
Android电话拨号器(uri格式)与四种设置点击事件的方法
查看>>
java web中对json的使用
查看>>
TYVJ P1051 选课 Label:多叉转二叉&&树形dp(虐心♥)
查看>>
将数据库中提取出来的数据在后台进行分页处理
查看>>
bzoj1034
查看>>
百度地图 鼠标绘制,获取矩形,多边形的顶点经纬度
查看>>
回文树模板
查看>>
struts2之防止表单重复提交
查看>>
【转】Netty系列之Netty并发编程分析
查看>>
cf591d
查看>>
图片存储系统TFS
查看>>
MYSQL备份与恢复
查看>>
贪心/数学 Codeforces Round #212 (Div. 2) A. Two Semiknights Meet
查看>>
Python类__call__()方法
查看>>
「小程序JAVA实战」 小程序wxss样式文件的使用(七)
查看>>