`
wangwengcn
  • 浏览: 173300 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Class文件加载过程

    博客分类:
  • JVM
阅读更多

1.加载
  (1)通过类的全限定名来获取此类的二进制字节流
  (2)将此字节流代表的静态存储结构转化为方法区的运行时数据结构
  (3)在Java堆中生成一个代表这个类的Class对象,作为方法区这些数据的访问入口
  
  2.验证
  (1)文件格式验证 :验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理
   是否以魔数0xCAFEBABE开头
   主次版本号是否在当前处理机处理范围之内
   常量池的常量中是否有不被支持的常量类型(检查常量tag标志)
   指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量
   CONSTANT_Utf8_info类型的常量中是否有不符合UTF8编码的数据
   Class文件中各部分及文件本身是否有被删除或附加的其他信息
   ……
  2)元数据验证 :对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求
   这个类是否有父类(除了Object之外,所有类都应当有父类)
   这个类的父类是否继承了不允许被继承的类(被final修饰的类)
   如果这个类不是抽象类,是否实现了其父类或接口中要求实现的所有方法
   类中的字段、方法是否与父类产生了矛盾(覆盖父类的final字段,不符合规则的方法重载)
   ……
  (3)字节码验证 :主要工作是进行数据流和控制流分析,保证被检验的类不会做出危害虚拟机的行为
   保证任意时刻操作数栈的数据类型与指令代码顺序都能配合工作
   保证跳转指令不会跳转到方法体之外的字节码指令上
   保证方法体的类型转换是有效的
   ……
  (4)符号引用验证 :在解析阶段中发生。
      符号引用验证可以看作是对类自身以外(常量池中各种符号引用)的信息进行匹配性的校验,通常需要校验以下内容:
   符号引用中通过字符串描述的全限定名是否能找到对应的类
   在指定类中是否存在符合方法的字段描述符及简单名称所描述的方法和字段
   符号引用中的类、字段和方法的访问性(private、protected、public、default)是否可被当前类访问
   ……
   可通过-Xverify:none来关闭大部分的类验证措施,以便缩短虚拟机类加载的时间
   
  3.准备
  准备阶段正式为类变量(static修饰的变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。
  这里需要注意的是,不对实例变量分配内存,实例变量将会在对象实例化的时候随着对象一起分配在Java堆中。
  比如:public static int value = 123;
  这里所指的初始值是给value赋0(boolean是false,reference是null),而123是要在类初始化(clinit)中赋的。
  
 4.解析
  解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,符号引用以CONSTANT_Class_info,CONSTANT_Fieldref_info,CONSTANT_Methodref_info等类型的常量出现。
  符号引用(Symbolic Reference):符号引用以一组符号来描述所引用的目标,符号引用可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可。
   符号引用与虚拟机实现的内存布局无关,引用的目标不一定已经加载到内存中。
  直接引用(Direct Reference):直接引用可以是指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机实现的内存布局相关的,
   同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经在内存中存在。
  解析动作主要针对类或接口、字段、类方法、接口方法四类符号引用进行,分别对应于常量池的CONSTANT_Class_info,CONSTANT_Fieldref_info,CONSTANT_Methodref_info,CONSTANT_InterfaceMethodref_info
  四种常量类型。
  (1)类或接口的解析
   假设当前代码所处的类为D,如果要把一个从未解析过的符号引用N解析为一个类或接口C的直接引用,那虚拟机完成整个解析过程需要包括以下三个步骤:
   1)如果C不是一个数组类型,那虚拟机将会把代表N的全限定名传递给D的类加载器其加载这个类C。在加载过程中,由于元数据验证、字节码验证的需要,又将可能触发其他相关类的加载动作(父类或接口)。
   2)如果C是一个数组类型,并且数组的原属类型为对象,也就是N的描述符会是类型"[Ljava.lang.Integer"的形式,那将会按照第一点的规则加载数组元素类型。
    如果N的描述符如前面所假设的形式,需要加载的元素类型就是"java.lang.Integer",接着由虚拟机生成一个代表此数组维度和元素的数组对象。
   3)如果上面的步骤没有出现任何异常,那么C在虚拟机中实际上已经成为一个有效的类或者接口了,但在解析完成之前还要进行符号引用验证,确认C是否具备对D的访问权限。
  (2)字段解析
   要解析一个未被解析过的字段符号引用,首先将会对字段表内的class_index项中索引的CONSTANT_Class_info符号引用进行解析,也就是字段所属的类或者接口的符号引用。
   如果解析完成,那将这个字段所属的类或接口用C表示,虚拟机规范要求按照如下步骤进行后续字段的搜索:
   1)如果C本身就包含了简单名称和字段描述符都与目标相匹配的字段,则返回这个字段的直接引用,查找结束。
   2)否则,如果在C中实现了接口,将会按照继承关系从上到下递归搜索各个接口和它的父接口,如果接口中包含了简单名称和字段描述都与目标相匹配的字段,则返回这个字段的直接引用,查找结束。
   3)否则,如果C不是Object的话,将会按照继承关系从上到下递归搜索其父类,如果在父类中包含了简单名称和字段描述符都与目标匹配的字段,则返回此字段,查找结束。
   4)否则,查找失败,抛出NoSuchFieldError
   如果成功找到该字段,将会对这个字段进行权限验证,如果发现不具备对字段的访问权限,将抛出IllegalAccessError
  (3)类方法解析
   类方法解析的第一个步骤与字段解析一样,也要先解析出类方法表的class_index项中索引的方法所属的类或接口的符号引用,如果解析成功,我们依然用C表示这个类,接下来虚拟机将会按照如下步骤进行后续的类方法搜索:
   1)类方法和接口方法符号引用的常量类型定义是分开的,如果在类方法表中发现class_index中索引C是个接口,那么直接抛出IncompatibleClassChangeError
   2)如果通过了第1)步,在类C中查找是否有简单名和描述符都与目标相匹配的方法,如果有则返回这个方法的直接引用,查找结束
   3)否则,在类C的父类中递归查找是否有简单名和描述符都与目标相匹配的方法,如果有则返回这个方法的直接引用,查找结束
   4)否则,在类C实现的接口列表及它们的父接口之中递归查找是否有简单名称和描述符都与目标相匹配的方法,如果存在匹配的方法,说明C是一个抽象类,这时候查找结束,抛出AbstractMethodError
   5)否则,宣告查找失败,抛出NoSuchMethodError
   如果成功找到了该方法,并返回直接引用,将会对这个方法进行权限验证,如果发现不具备对此方法的访问权限,将抛出IllegalAccessError
  4)接口方法解析
   接口方法也是需要先解析出接口方发表的class_index项中索引的方法所属的类或者接口的符号引用,如果解析成功,依然用C表示这个接口,接下来虚拟机将按照如下的步骤进行后续方法搜索:
   1)与类方法相反,如果在接口方法中表中发现class_index中的索引C是一个类而不是接口,那就直接抛出IncompatibleClassChangeError
   2)否则,在接口C中查找是否有简单名称和描述符都与目标相匹配的方法,如果有则返回这个方法的直接引用,查找结束
   3)否则,在接口C的父接口中递归查找,直到Object(查找范围会包括Object类)位置,看是否有简单名称和描述符都与目标相匹配的方法,如果有则返回这个方法的直接引用,查找结束
   4)否则,宣告方法查找结束,抛出NoSuchMethodError
   由于接口中所有的方法都默认是public的,所以不存在访问权限问题
  5.初始化

    本过程在有任何调用本类的(参数或者方法)前提下才进行,如果仅仅是loadclass,不触发
  类初始化是类加载过程的最后一步,前面的类加载过程中,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java代码。
  在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,则是根据程序员通过程序制定的主观计划去初始化类变量和其他资源,或者从另一个角度来说:初始化阶段是执行类构造器clinit方法的过程。
  1)clinit方法是由编译期自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的,编译期收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在它之前的变量,
   定义在它之后的变量,在前面的静态语句块中能赋值,但不能访问。
  2)clinit方法与实例构造器init不同,它不需要显式的调用父类构造器,虚拟机会保证在子类的clinit执行之前,父类的clinit已经执行完毕。因此在虚拟机中第一个被执行的clinit方法的类肯定是Object
  3)由于父类的clinit方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作
  4)clinit方法对于类或者接口来说并不是必须的,如果一个类中没有静态语句块,也没有对类变量的赋值操作,那么编译器可以不生成clinit方法
  5)接口中不能使用静态语句块,但仍然有变量赋值操作,因此接口与类一样都会生成clinit方法。但是接口与类不一样,执行接口的clinit不需要先执行父接口的clinit。只有父接口的变量被使用时父接口才被初始化。
   另外,接口的实现类在初始化的时候也一样不会执行接口的clinit
  6)虚拟机会保证一个类的clinit在多线程环境中被正确的加锁和同步(执行一次)

分享到:
评论

相关推荐

    通过实例解析Java class文件编译加载过程

    主要介绍了通过实例解析Java class文件编译加载过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    java class加密

    Java Class加密器是广州市敏创信息科技有限公司在保护自己的...由于Java工程中,还存在不少配置文件等,为了保证工程的完整性,可以勾选“复制非Class文件”,加密时,会将源文件路径中所有非Class文件复制到目标路径。

    asp.net core配置文件加载过程的深入了解

    作为一个入口,可以让我们在不更新代码的情况,对程序进行干预和调整,那么对其加载过程的全面了解就显得非常必要。 何时加载了默认的配置文件 在 Program.cs 文件中,查看以下代码 public class Program { ...

    Java加载。jar包

    让我们假设有一个class字节码文件(比如Hello.class文件),那么在应用程序中,他是如何被加载进来,并形成一个类对象的呢?我们这篇文章的目的就是为了解释这个问题。 在java.lang包里有个ClassLoader类,...

    Java学习代码实例 JAVA基础,JAVA面向对象,JAVA高级.rar

    JAVA命令->要使用一个*.class文件(类文件)->通过classpath找到*.class文件所保存的真实目录,之后再进行加载执行 小结 Path的主要功能是设置JDK的可执行命令 classpath主要是在执行时候起作用,告诉JVM类的保存路径...

    ClassFinal字节码加密工具-其他

    ClassFinal是一款Java class文件安全加密工具,支持直接加密jar包或war包,无需修改任何项目代码,兼容spring-framework,可避免源码泄漏或字节码被反编译。项目模块说明:classfinal-core:ClassFinalde的核心模块...

    JVM的类加载过程以及双亲委派模型详解

    主要介绍了JVM的类加载过程以及双亲委派模型详解,类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。,需要的朋友可以参考下

    【JVM】类加载器与双亲委派模型

    类加载器在加载阶段,会将class文件加载进方法区。有关类加载的全过程,可以先参考我的另外一篇文章类的奇幻漂流——类加载机制探秘 类加载器的类型 类加载器有以下种类: 启动类加载器(Bootstrap ClassLoader) ...

    java类热加载

    实现对代码的动态更新,只需要上传覆盖原class文件即可在不停止项目的过程中更新代码

    深入理解Java类加载.docx

    虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这就是虚拟机的类加载机制。 在Java语言里面,类型的加载、连接和初始化过程都是在...

    详解JAVA类加载机制(推荐)

    主要是将源码编译成字节码文件(class文件)。字节码文件格式主要分为两部分:常量池和方法字节码。 二、类加载 类的生命周期是从被加载到虚拟机内存中开始,到卸载出内存结束。过程共有七个阶段,其中到初始化之前...

    类加载与类初始化

    JVM把class文件加载到内存,并对数据进行校验、解析和初始化,最终形成 JVM可以直接使用的Java类型的过程。 \quad·加载 \quad\quad将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时...

    java加载机制.png

    1.java源文件是怎么编译成class文件的 2.类的生命周期 3.java类加载机制 4.类的加载 5.类的加载过程 6.类是怎么被初始化的? .....

    MiniJVM:MiniJVM是用python语言编写的简单Java虚拟机,它可以从文件系统中加载类文件并运行它

    MiniJVMMiniJVM是一款使用python编写的简易JVM,...如下:[-h] ==> 查看帮助[-v] ==> 查看版本信息[-d] ==> 开启指令执行debug,开启之后可以看到类加载和指令执行的过程[-cp] ==> class文件的目录路径[-cn] ==> 目标

    Java虚拟机类加载机制浅谈

     虚拟机将描述类的数据从Class文件加载到内存,并对数据进行校验、准备、解析和初始化,终会形成可以被虚拟机使用的Java类型,这是一个虚拟机的类加载机制。Java中的类是动态加载的,只有在运行期间使用到该类的...

    加密解密文件C#+unity

    本例子详细解释了unity中的assetbundle加密解密加载过程

    Java代码保护工具全套源码(jShield)

    jShield是本人基于自定义类加载器技术开发的Java代码保护工具,可以加密保护Jar包或Class文件,适合需要保护Java程序的朋友使用和研究。 jShield是一款WinForm工具,通过图形化界面导入jar包或class文件后,即可...

    字节码文件结构详解.docx

    在学习 Java 之初,就了解到了我们所写的.java会被编译期编译成.class文件之后被 JVM 加载运行。JVM 全称为 Java Virtual Machine,一直以为 JVM 执行 Java 程序是一件理所当然的事情,但随着工作过程中接触到了...

    JVM学习笔记(一)——类的加载机制

    ​ 类加载器并不需要等到某个类被“首次主动使用”时再加载它,JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动...

    Day 24反射

    1.2 程序加载过程和.class文件的关系 在Java文件运行过程中,当前程序需要哪一个类参与代码执行,那么就需要加载这个类的.class字节码文件,该.class字节码文件时在程序的加载阶段,存在于内存的【代码区】 .class...

Global site tag (gtag.js) - Google Analytics