类加载机制和类加载过程

类加载器机制

种类:

  • 启动类加载器
  • 扩展类加载器
  • 应用程序类加载器
  • 自定义类加载器

类加载器可以大致划分为以下三类 :

  • 启动类加载器(Bootstrap ClassLoader):负责加载存放在JDK\jre\lib(JDK代表JDK的安装目录,下同)下,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.开头的类均被 Bootstrap ClassLoader 加载 ) 。启动类加载器是无法被Java 程序直接引用的。

  • 扩展类加载器(Extension ClassLoader):负责加载JDK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。

  • 应用程序类加载器( Application ClassLoader):负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如 果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

应用程序都是由这三种类加载器互相配合进行加载的,如果有必要,我们还可以加入自定义的类加载器。因为JVM自带的ClassLoader只是懂 得从本地文件系统加载标准的java class文件,因此如果编写了自己的ClassLoader,便可以做到如下几点:

  • 在执行非置信代码之前,自动验证数字签名。

  • 动态地创建符合用户特定需要的定制化构建类。

  • 从特定的场所取得java class,例如数据库中和网络中

双亲委派机制

  • 加载一个类时,JVM会从最底层
  • 自底向上查看这些加载器,是否曾经加载过这个类
  • 如果四个加载器均没有加载过这个类,JVM就会
  • 自顶向下的尝试去加载这个类
工作原理

先检查有没有加载过这个类

如果一个类收到了类加载的请求,它并不会自己先去加载,而是把这个请求委托给父类加载器去执行,如果父类加载器还存在父类加载器,则进一步向上委托,依次递归,请求最后到达顶层的启动类加载器,顶层的启动类加载器如果能够完成类的加载任务,就会成功加载,倘若父类加载器无法完成任务,子类加载器才会尝试自己去加载,这就是双亲委派模式。

优势

1、性能:首先它可以避免类的重复加载,当父亲已经加载了该类的时候,就没有必要子类加载器(ClassLoader)再加载一次。

2、安全:其次是考虑到安全因素,Java核心API中定义类型不会被随意替换,假设通过网路传递一个名为java.lang.Integer的类,通过双亲委派的的模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字类,发现该类已经被加载,并不会重新加载网络传递过来的java.lang.Integer.而之际返回 已经加载过的Integer.class,这样便可以防止核心API库被随意篡改。

破坏双亲委派模型

第一次破坏

双亲委派模型是JDK1.2之后才引入的,在之前已经有了类加载器抽象类java.lang.ClassLoader,为了兼容已经存在的用户自定义类加载模型,所以破坏

第二次破坏

第二次破坏是由这个模型本身的缺陷导致的。
一般情况是 基础类总是被用户调用,
但是在有需求,基础类需要调回用户代码时 需要再次破坏

第三次破坏

第三次“被破坏”是由于用户对程序动态性的追求导致的。
像代码热替换、模块热部署等,就是不需要重启就可以实现代码替换

类加载过程

请添加图片描述

1、加载
该过程是类加载的一个阶段

就是一个从二进制字节流转变成能适应java虚拟机java.lang.Class对象的过程

具体:
1、先通过一个类的全限定名, 获得它的二进制字节流;
2、把这个字节流的存储形式转化为方法区运行时的数据结构
3、在内存中生成一个代表这个类的java.lang.Class对象,然后就可以作为入口访问这个类了

2、验证
目的:确保Class文件的字节流中里面的信息符合《Java虚
拟机规范》 标准。 其实就是一个检查过程,保证各种方法不会做出危害虚拟机的行为。

具体:

  • 文件格式验证: 验证字节流是否符合Class文件格式的规范;例如: 是否以0xCAFEBABE开头、主次版本号是否在当前虚拟机的处理范围之内、常量池中的常量是否有不被支持的类型。

  • 元数据验证: 字节码描述的信息进行语义分析(注意: 对比javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求;例如: 这个类是否有父类,除了java.lang.Object之外。

  • 字节码验证: 通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
    比如:例如不会出现类似于“在操作栈放置了一个int类型的数据, 使用时却按long类型来加载入本地变量表中”这样的情况

  • 符号引用验证: 确保解析动作能正确执行。

像private、public、protected之类修饰的,可不可以被当前类访问
通过字符串描述的全限定名是否能找到对应的类。

3、准备
为static变量在方法区中分配内存空间,设置变量的初始值,例如 static int a = 3 (注意:准备阶段只设置类中的静态变量(方法区中),不包括实例变量(堆内存中),实例变量是对象初始化时赋值的)

  • 这个时候static int a=3; a的值是 0 在接下来的初始化阶段才会赋值成 3
  • 同时被final修饰的会被赋值 final int b=4; //b

4、解析
虚拟机将常量池内的符号引用替换为直接引用的过程(符号引用比如我现在import java.util.ArrayList这就算符号引用,直接引用就是指针或 者对象地址,注意引用对象一定是在内存进行)

符号引用 就是一组符号来描述目标,可以是任何字面量。

直接引用 就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。

5、初始化

为类的静态变量赋予正确的初始值

两种方式:

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

JVM初始化步骤:

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

类初始化时机 : 只有当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:

  • 创建类的实例,也就是new的方式
  • 访问某个类或接口的静态变量,或者对该静态变量赋值
  • 调用类的静态方法
  • 反射(如Class.forName(“com.pdai.jvm.Test”))
  • 初始化某个类的子类,则其父类也会被初始化
  • Java虚拟机启动时被标明为启动类的类(Java Test),直接使用java.exe命令来运行某个主类

使用

类访问方法区内的数据结构的接口, 对象是Heap区的数据。

卸载
Java虚拟机将结束生命周期的几种情况

执行了System.exit()方法
程序正常执行结束
程序在执行过程中遇到了异常或错误而异常终止
由于操作系统出现错误而导致Java虚拟机进程终止
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值