类加载过程

类加载器子系统

在这里插入图片描述
系统加载 Class 类型的文件主要三步:加载->连接->初始化。连接过程又可分为三步:验证->准备->解析。

加载

类加载过程的第一步,主要完成下面3件事情:

  • 通过全类名获取定义此类的二进制字节流
  • 将字节流所代表的静态存储结构转换为方法区的运行时数据结构
  • 在内存中生成一个代表该类的 Class 对象,作为方法区访问这些数据的访问入口

Java加载.class文件的几种方式:

  • 从本地加载.class文件
  • 从网络加载.class文件
  • 从本地加载.zip、.jar等归档文件
  • 从专用数据库提取.class文件
  • 从内存中加载.class文件(.java文件动态编译)
  • 从加密文件中获取,防止class文件被反编译的保护措施

连接阶段

验证

确保当前class文件的字节流所包含的内容符合当前JVM的规范要求,并且不会出现危害JVM自身安全的代码,当前字节流不符合规范会抛出VerifyError的异常,或者子异常.

验证的信息有:

  • 文件格式:验证二进制文件是什么类型,验证是否符合当前JVM规范
  • 元数据验证:检查类是否有父类、接口/验证其父类、接口的合法性、验证被final修饰的类、验证是否是抽象类,是否实现了父类的抽象方法或者接口中的方法、 验证方法的重载。
  • 字节码验证:主要验证程序的控制流程比如循环、分支等
  • 符号验证:主要验证符号引用转换为直接引用时的合法性

准备

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。

注意:

  • 这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在 Java 堆中。
  • 这里所设置的初始值"通常情况"下是数据类型默认的零值(如0、0L、null、false等),这里不包含final修饰的static,因为final在编译时就分配了,准备阶段会显示初始化。

基本数据类型的零值:

在这里插入图片描述
解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符7类符号引用进行。

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,也就是得到类或者字段、方法在内存中的指针或者偏移量。

初始化

初始化是类加载的最后一步,也是真正执行类中定义的 Java 程序代码(字节码),初始化阶段是执行类构造器
clinit ()方法的过程。

注意:

  • clinit()方法在编译过程中自动生成,此方法不需要定义,此方法中包含了所有类变量的赋值以及静态代码语句块的执行代码
  • 编译器收集的顺序是由执行语句在源文件中的出现顺序来决定的,静态语句块只能对后面的静态变量进行赋值,而不能对其进行访问
  • clinit()不同于类的构造器。(构造器是VM视角下的init()方法)
  • 若该类具有父类,JVM保证子类的clinit()执行前,父类的clinit()已经执行完毕
  • VM必须保证一个类的clinit()方法在多线程下被同步加锁。

对于clinit() 方法的调用,虚拟机会自己确保其在多线程环境中的安全性。因为 clinit() 方法是带锁线程安全,所以在多线程环境下进行类初始化的话可能会引起死锁,并且这种死锁很难被发现。
在同一时间,只能有一个线程执行到静态代码块中的内容,并且静态代码块仅仅执行一次,JVM保证了clinit()方法在多线程的执行环境下的同步语义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值