首页 > 其他 > 详细

JVM类加载

时间:2020-05-25 11:15:12      阅读:47      评论:0      收藏:0      [点我收藏+]

基本概念

JRE & JDK & JVM

  • JRE(Java运行环境):所有的Java 程序都要在JRE下才能运行。
  • JDK:开发者编译、调试java程序用的开发工具包。JDK的工具也是Java程序,也需要JRE才能运行。在JDK的安装目录下有一个名为jre的目录,用于存放JRE文件。
  • JVM(Java虚拟机)是JRE的一部分。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。使用JVM就是为了支持与操作系统无关,实现跨平台。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。

JVM类加载

技术分享图片

加载

技术分享图片
数组类与类加载器仍然有很密切的关系,因为数组类的元素类型最终是要靠类加载器去创建的,
数组创建过程如下:

  • 如果数组的组件类型是引用类型,那就递归采用类加载加载。
  • 如果数组的组件类型不是引用类型,Java 虚拟机会把数组标记为引导类加载器关联。
  • 数组类的可见性与他的组件类型的可见性一致,如果组件类型不是引用类型,那数组类的可见性将默认为 public。

验证

技术分享图片
java class文件格式可以参考:java class 字节码

  • 文件格式验证:验证字节流是否符合Class文件格式的规范,而且能被当前版本的虚拟机处理
    • 是否以0xCAFEBABE开头。
    • 主、次版本号是否在当前虚拟机的处理范围之内。
    • 常量池中的常量是否有不被支持的常量类型。
    • 指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量。
    • Class文件中各个部分及文件本身是否有被删除的或附加的其他信息。
  • 元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求
    • 这个类是否有除了java.lang.Object之外的父类。
    • 这个类的父类是否继承了不允许被继承的类(被final修饰的类)。
    • 如果这个类不是抽象类,是否实现了其父类或接口中要求实现的所有方法。
    • 类中的字段、方法是否与父类产生矛盾。
  • 字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的
    • 保证任意时刻操作数栈的数据类型与指令代码序列都能配合工作
    • 保证跳转指令不会跳转到方法体以外的字节码指令上
    • 保证方法体中的类型转换是有效的
  • 符号引用验证
    • 符号引用中通过字符串描述的全限定名是否能找到对应的类。
    • 在指定类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段。
    • 符号引用中的类、字段、方法的访问性是否可被当前类访问。

准备

技术分享图片
为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中分配

  • 这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中
  • 这里所设置的初始值通常是数据类型默认的初始值,而不是被在Java代码中被显式地赋予的值
    • 对基本数据类型来说,对于类变量(static)和全局变量,如果不显式地对其赋值而直接使用,则系统会为其赋予默认的零值,而对于局部变量来说,在使用前必须显式地为其赋值,否则编译时不通过
    • 对于同时被static和final修饰的常量,必须在声明的时候就为其显式地赋值,否则编译时不通过;而只被final修饰的常量则既可以在声明时显式地为其赋值,也可以在类初始化时显式地为其赋值,总之,在使用前必须为其显式地赋值,系统不会为其赋予默认零值
    • 对于引用数据类型来说,如数组引用、对象引用等,如果没有对其进行显式地赋值而直接使用,系统都会为其赋予默认的零值,即null
    • 如果在数组初始化时没有对数组中的各元素赋值,那么其中的元素将根据对应的数据类型而被赋予默认的零值

解析

技术分享图片

  • 类或接口的解析
  • 字段解析
    • 查找失败 java.lang.NoSuchFieldError
    • 查找成功,没有权限 java.lang.IllegalAccessError
  • 类方法解析
    • 查找失败 java.lang.NoSuchMethodError
    • 查找成功,没有权限 java.lang.IllegalAccessError
  • 接口方法解析
    • 查找失败 java.lang.NoSuchMethodError

初始化

初始化阶段是执行类构造器方法的过程。方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证方法执行之前,父类的方法已经执行完毕。如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器可以不为这个类生成()方法
技术分享图片
Java中对类变量进行初始值设定有两种方式:

  • 声明类变量时指定初始值
  • 使用静态代码块为类变量指定初始值

JVM初始化步骤

  • 假如这个类还没有被加载和连接,则程序先加载并连接该类
  • 假如该类的直接父类还没有被初始化,则先初始化其直接父类
  • 假如类中有初始化语句,则系统依次执行这些初始化语句

什么时候需要对类进行初始化

  • 使用new该类实例化对象的时候;
  • 读取或设置类静态字段的时候(但被final修饰的字段,在编译器时就被放入常量池的静态字段除外static final);
  • 调用类静态方法的时候;
  • 使用反射Class.forName(“xxxx”)对类进行反射调用的时候,该类需要初始化;
  • 初始化一个类的时候,有父类,先初始化父类(注:1. 接口除外,父接口在调用的时候才会被初始化;2.子类引用父类静态字段,只会引发父类初始化);
  • 被标明为启动类的类(即包含main()方法的类)要初始化;

以下不会对类进行初始化

  • 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
  • 定义对象数组,不会触发该类的初始化。
  • 常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类。
  • 通过类名获取Class对象,不会触发类的初始化。
  • 通过Class.forName加载指定类时,如果指定参数initialize为false时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。
  • 通过ClassLoader默认的loadClass方法,也不会触发初始化动作

JVM类加载

原文:https://www.cnblogs.com/cloud-blog/p/12934201.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!