深入理解Java虚拟机之虚拟机执行子系统 (2)

1、概述

  • 虚拟机的类加载机制: 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化、最终形成可以直接被虚拟机直接使用的java类型,这个过程被称作虚拟机的类加载机制。

2、类加载的时机

  • 一个类型从加载到虚拟机内存开始,到卸载出内存为止,他的整个生命周期经历加载、验证、准备、解析、初始化、使用、卸载七个阶段。
  • 验证、准备、解析三个部分统称为连接。
  • 加载、验证准备、初始化和卸载这五个阶段的顺序是确定的,类型的加载必须按照这种顺序按部就班的开始,解析则不一定。
  • 《Java虚拟机规范》则是严格规定了有且只有六种情况必须立即对类进行“初始化”。(加载、验证、准备是在此之前开始的)(转载)六种初始化场景
  • 除此之外,所有的引用类型的方式都不会触发初始化,称为被动引用。
  • 接口与类真正有所区别的前面六种中需要直接触发初始化场景中第三种

在这里插入图片描述

3、类加载的过程

1、加载

  • 加载阶段做的三件事:
    • 通过一个类的全限定名来获取定义此类的二进制字节流。
    • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据区。
    • 在内存种生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问路口。
  • 数组有所不同,数组是由Java虚拟机直接在内存中动态构造出来的。
  • Java虚拟机外部的二进制字节流按照虚拟机所设定的格式存储在方法区中。类型数据妥善安置在方法区之后,会在Java堆中实例化一个java.lang.Class类的对象,这个对象作为程序访问方法区中的类型数据的外部接口。

2、验证

  • 验证是连接的第一步,这一阶段的目的就是确定Class文件中的字节流包含的信息符合了《Java虚拟机规范》的全部要求,确保这些信息被当作代码运行后不会危害虚拟机。
  • 大致上分为四个阶段的检验动作
    • 文件格式验证:验证字节流是否符合Class文件格式的规范
    • 元数据验证:对字节码描述的信息进行语义分析
    • 字节码验证:(最复杂) 通过数据流分析和控制流分析,确定程序语义是合法的。(对类的方法体,Class文件中code属性进行校验分析)
    • 符号引用验证 :对类自身以外(常量池中的各种符号引用)的各类信息进行匹配性校验。

3、准备

  • 正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。

4、解析

  • Java虚拟机将常量池分符号引用(一组符号来描述所引用的目标)替换为直接引用(直接指向目标的指针,相对偏移量或一个能间接定位到目标的句柄)的过程。
  • 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符这七类符号

5、初始化

  • 初始化阶段,Java虚拟机才真正开始执行类中编写的Java程序代码,将主导权移交给应用程序。
  • 初始化阶段就是执行类构造器()方法的过程。(()是编译器自动收集类中所有类变量的赋值动作和静态语句块static{} 中的语句合并产生的, 父类的此方法先于子类执行)、
  • 接口与类不都会生成(()方法),接口与类不同的是,执行接口的方法不需要先执行父接口的方法,只有当父接口中定义的变量被使用时,接口才会被初始化
  • 对于类或接口来说并不是必须的,如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生成方法
  • Java虚拟机必须保证一个类的方法在多线程环境中被正确地加锁同步

4、类加载器

  • 有意将类加载阶段中的“通过一个类的全限定名来获取描述该类的二进制字节流”,这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需的类,实现这个动作的代码被称为“类加载器”

1、类与类加载器

  • 对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其Java虚拟机中的唯一性。

2、双亲委派模型

  • 启动类加载器。这个类加载器是由C++语言实现,是虚拟机自身的一部分。
  • 其他所有的类加载器,这些类加载器是由Java语言实现,独立于虚拟机外部,并且全部继承自抽象类java.lang.ClassLoader。
  • 启动类加载器 :<java_home>\lib目录或者 被-Xbootclasspath 指定的路径存放的
  • 扩展类加载器:在sum.misc.Launcher$ExtClassLoader中以Java代码的形式存在的,负责加载<java_hone>\lib\ext目录 或者被java.ext.dirs系统变量指定的路劲中所有的类库。
  • 应用程序类加载器:sum.misc.Launcher$AppClassLoader,负责加载用户类路劲上的所有的类库。

(Java9之前 类加载器的双亲委派模型)
在这里插入图片描述

  • 双亲委派模型:
    • 双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器,这里父子关系一般不是以继续的关系实现的,通常是使用组合关系来复用父加载器的代码
    • 双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类),子加载器才会从尝试自己去加载。
  • 破坏双亲委派模型(三次较大的破坏)
    • (JDK1.2之前按远古时代,用户自定义类加载器的代码)解决方式:用java.lang.ClassLoader中受保护的findClass去编写;(双亲委派的具体逻辑写在loadClass方法中)
    • JNDI服务 :使用线程上下文类加载器(java.lang.Thread类的setContextClassLoader方法),如果线程未创建,将会从父线程选择一个,全局没有则默认应用程序类加载器。
    • 用户对于程序动态性的追求而导致的:代码热部署、模块热部署

5、Java模块化系统

模块化的关键目标:可配置的封装隔离机制。
(转)使用
(转)模块化简要介绍

参考书籍《深入理解Java虚拟机》第三版

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值