资讯专栏INFORMATION COLUMN

深入理解Java虚拟机05--虚拟机类加载机制

yanbingyun1990 / 2182人阅读

摘要:我们甚至可以从网络或者其他的地方加载一个二进制流作为程序的一部分。对于任何一个类,我们通过类和这个类的加载器共同确定在中的唯一性,为了保证父类和子类的层次关系。

一.前言
  我们一定心里有个疑问,我们那个多态是怎么回事?我们指定的一个接口,却可以等到运行时可以对应于不同的实现类。这是因为,Java有个特性就是依赖运行期动态加载和动态连接,这样实现了Java可以动态进行扩展。我们甚至可以从网络或者其他的地方加载一个二进制流作为程序的一部分。所以,我们通过编译器将我们写的Java文件代码编译成Class文件,程序跑起来的时候通过加载器。

二.加载过程

 

1、 加载(loading)
三件大事

  • 1、通过类的全限定名来定义这个类的二进制流
  • 2、将字节流代表的静态存储结构变成方法区的运行时数据结构
  • 3、在方法区生成一个java.lang.Class对象,作为方法区数据的访问入口

自定义类加载器:重写loadClass()方法

  • 一个区别:数组类本身不通过类加载器创建,而是由虚拟机直接创建,但是数组的元素还是需要类加载器创建的;

2、 连接
(1)、 验证

  • 确保Class文件的字节流中包含的信息符合当前虚拟机的要求,避免导致系统奔溃
  • 验证类型
    • 文件格式验证:保证输入的字节流能够正确的解析并存储于方法区之内,格式上符合Java类型信息;
    • 元数据验证:对元数据信息进行语义上的校验
    • 字节码验证:通过数据流和控制流分析,确定程序语义是合法的,保证方法运行时不会危害虚拟机;
    • 符号引用验证:验证类是否找到到(NoSuchMothodError),访问性是否正常等等,保证解析动作能正常运行;
  • 验证是重要但不是必须的,对于反复验证呢过的Class可以考虑使用-Xverify:none参数来关闭大部分的类验证措施;

(2)、准备:为类变量分配内存并设置初始值(如int为0)。
(3)、解析:

  • 可以放在初始化之后进行,比如需要动态加载的情况下
  • 将常量池中的符号引用替换为直接引用的过程
    • 符合引用:一组符号来描述所引用目标,与虚拟机内存布局无关;
    • 直接引用:直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。和内存布局有关,目标已经在内存中;
  • invokedynamic:动态调用点限定符,程序实际运行到这条指令的时候,解析动作才能进行;
  • 解析种类
    • 类或接口的解析
    • 字段解析
    • 类方法解析
    • 接口方法解析

3、初始化

  • 必须要立即进行初始化的情况如下(主动引用:主动触发引用的类进行初始化):
    • new(实例化对象)、getstatic、putstatic(读取或者设置静态字段)、invokestatic(调用类静态方法)这4个字节码指令
    • java.lang.reflect包的方法反射调用,类若没有初始化必须先进行初始化
    • 初始化一个类,父类没有初始化,必须先初始化父类(但是接口不用,只有用到父接口时,才会初始化)
    • 虚拟机指定了main方法的类
    • JDK1.7,java.lang.invoke.MethodHandle解析的句柄为REF_ getStatic、REF_ putStatic、REF_ invokeStatic,对应的类还没有被初始化
  • 被动引用
    • 子类引用父类的静态字段,只初始化父类,而不会触发初始化子类
    • 通过数组定义来引用类,不会触发此类的初始化
    • 常量在编译阶段会存入到调用类的常量池中,本质上没有直接应用调用到定义了常量的类,因此定义了常量的这个类不会触发初始化
  • < cinit >()方法:编译器自动收集类中的类变量的赋值动作和static代码块中的程序语句,并且能自动触发父类的< cinit >()方法先进行初始化。遇到多线程并发的时候,会自动加锁,其他的线程会被阻塞。直到执行完毕。

4、 使用
5、 卸载

三.类加载器

1、唯一性

  • 对于任何一个类,都需要和这个类的加载器与这个类共同确定在Java虚拟机中的唯一性,这里说的唯一性指的是“相等”,也就是我们平时说的Class对象的equals()、isAssiganableFrom()、isInstance()方法的返回结果;

2、双亲委派模型

  • 特点:

当一个类的加载器收到了加载请求,不会自己先动手,而是委派给这个类的父类进行加载,如果找不到加载不了就反馈回来自己加载。这样的话,让Java的类一出生就有了很好的层次父子关系。当然也有一些手段去破坏这种关系而获得某种效果。

  • 破坏:

双亲委派模型可以被破坏,推荐重写findClass()方法,而不是loadClass(),应用于热部署等技术;

四.小结

  我们通过编译器先将我们写的.java代码编译为可执行的.class文件,那么如果我们需要真正的执行这个代码,还需要一个过程。这个时候加载器的角色就来了,加载器将首先要加载可执行文件,并变换数据结构。在初始化之前,我们还需要进行验证和准备。解析的过程可以在初始化之前,也可以在初始化之后(实现动态加载的时候—)。触发初始化的条件有几种,分为被动引用和主动引用两大类。我们可以理解为主动引用是我们主动的触发了本Class的初始化,比如New 这个对象的实例。 但是,也存在我们在本类中引用到了其他的类,比如说父类,其他类的常量。如果,我们的操作不是上述的主动引用,其结果是没有触发本Class的初始化,而是间接的触发了别的Class进行初始化工作。我们称这个为被动引用。对于任何一个类,我们通过类和这个类的加载器共同确定在JVM中的唯一性,为了保证父类和子类的层次关系。我们在有需求触发子类的初始化时,必须先完成父类的初始化工作,一直向上追溯,从上到下依次完成初始化。这就是所谓的双亲委派模型。双亲委派模型也是可以被破坏的,在热部署技术中有应用。

 

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/2854.html

相关文章

  • 深入理解虚拟虚拟加载

    摘要:最终形成可以被虚拟机最直接使用的类型的过程就是虚拟机的类加载机制。即重写一个类加载器的方法验证验证是连接阶段的第一步,这一阶段的目的是为了确保文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。 《深入理解Java虚拟机:JVM高级特性与最佳实践(第二版》读书笔记与常见相关面试题总结 本节常见面试题(推荐带着问题阅读,问题答案在文中都有提到): 简单说说类加载过...

    MadPecker 评论0 收藏0
  • 虚拟加载

    摘要:加载阶段在类的加载阶段,虚拟机需要完成以下件事情通过一个类的全限定名来获取定义此类的二进制字节流。验证阶段验证是连接阶段的第一步,这一阶段的目的是为了确保文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。 注:本篇文章中的内容是根据《深入理解Java虚拟机--JVM高级特性与最佳实践》而总结的,如有理解错误,欢迎大家指正! 虚拟机把描述类的数据从Class文件...

    k00baa 评论0 收藏0
  • Java虚拟加载过程

    摘要:二验证验证主要是为了确保文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的自身安全。五初始化类的初始化阶段是类加载过程的最后一步,该阶段才真正开始执行类中定义的程序代码或者说是字节码。 关注我,每天三分钟,带你轻松掌握一个Java相关知识点。 虚拟机(JVM)经常出现在我们面试中,但是工作中却很少遇到,导致很多同学没有去了解过。其实除了应付面试,作为java程序员,了解...

    lentoo 评论0 收藏0
  • JAVA 虚拟加载制和字节码执行引擎

    摘要:实现这个口号的就是可以运行在不同平台上的虚拟机和与平台无关的字节码。类加载过程加载加载是类加载的第一个阶段,虚拟机要完成以下三个过程通过类的全限定名获取定义此类的二进制字节流。验证目的是确保文件字节流信息符合虚拟机的要求。 引言 我们知道java代码编译后生成的是字节码,那虚拟机是如何加载这些class字节码文件的呢?加载之后又是如何进行方法调用的呢? 一 类文件结构 无关性基石 ja...

    RichardXG 评论0 收藏0
  • 虚拟加载

    摘要:虚拟机为了保证一个类的方法在多线程环境中被正确地加锁同步。但启动类加载器不可能认识这些代码。实现模块化热部署的关键则是它的自定义类加载器机制的实现。 概念区分:加载、类加载、类加载器 类加载是一个过程。 加载(Loading)是类加载这一个过程的阶段。 类加载器是ClassLoader类或其子类。 本文中的类的描述都包括了类和接口的可能性,因为每个Class文件都有可能代表J...

    airborne007 评论0 收藏0

发表评论

0条评论

yanbingyun1990

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<