开发者社区> javaedge> 正文

JVM类加载、验证、准备、解析、初始化、卸载过程详解(下)

简介: JVM类加载、验证、准备、解析、初始化、卸载过程详解(下)
+关注继续查看

3 准备

完成两件事情

  • 为已在方法区中的类的静态成员变量分配内存
  • 为静态成员变量设置初始值
    初始值为0、false、null等
  • image.png
public static final int value = 123;

准备阶段后 a 的值为 0,而不是 123,要在初始化之后才变为 123,但若被final修饰的常量如果有初始值,那么在编译阶段就会将初始值存入constantValue属性中,在准备阶段就将constantValue的值赋给该字段(此处将value赋为123).

4 解析

把常量池中的符号引用转换成直接引用的过程。包括:

  • 符号引用
    以一组无歧义的符号来描述所引用的目标,与虚拟机的实现无关。
  • 直接引用
    直接指向目标的指针、相对偏移量、或是能间接定位到目标的句柄,是和虚拟机实现相关的。

主要针对:类、接口、字段、类方法、接口方法、方法类型、方法句柄、调用点限定符。

5 初始化

真正开始执行类中定义的Java程序代码(或说是字节码),类的初始化就是为类的静态变量赋初始值,初始化阶段就是执行类构造器<clinit>的过程。


如果类还没有加载和连接,就先加载和连接

如果类存在父类,且父类没有初始化,就先初始化父类

如果类中存在初始化语句,就依次执行这些初始化语句

如果是接口

初始化一个类时,并不会先初始化它实现的接口

初始化一个接口时,并不会初始化它的父接口

只有当程序首次使用接口里面的变量或者是调用接口方法的时候,才会导致接口初始化

调用Classloader类的loadClass方法来装载一个类,并不会初始化这个类,不属于对类的主动使用

clinit()方法由编译器自动产生,收集类中static{}代码块中的类变量赋值语句和类中静态成员变量的赋值语句。

在准备阶段,类中静态成员变量已经完成了默认初始化,而在初始化阶段,clinit()方法对静态成员变量进行显示初始化。

类的初始化时机

Java程序对类的使用方式分为:

  • 主动使用
  • 被动使用

JVM必须在每个类或接口“首次主动使用”时才初始化它们,被动使用类不会导致类的初始化。主动使用的场景:


创建类实例

访问某个类或接口的静态变量

如果是 final 常量,而常量在编译阶段就会在常量池,没有引用到定义该常量的类,因此不会触发定义该常量类的初始化

调用类的静态方法

反射某个类

初始化某个类的子类,而父类还没有初始化

JVM启动的时候运行的主类(等于第三条)

定义了 default 方法的接口,当接口实现类初始化时

初始化过程的注意点

  • clinit()方法是IDE自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的,IDE收集的顺序是由语句在源文件中出现的顺序所决定的.
  • 静态代码块只能访问到出现在静态代码块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问.
public class Test {
    static {
        i=0;
        System.out.println(i); //编译失败:"非法向前引用"
    }
    static int i = 1;
}

实例构造器init()需要显式调用父类构造器,而类的clinit()无需调用父类的类构造器,JVM会确保子类的clinit()方法执行前已执行完毕父类的clinit()方法。

因此在JVM中第一个被执行的clinit()方法的类肯定是java.lang.Object.

如果一个类/接口无static代码块,也无 static成员变量的赋值操作,则编译器不会为此类生成clinit()方法

接口也需要通过clinit()方法为接口中定义的static成员变量显示初始化。

接口中不能使用静态代码块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成clinit()方法.不同的是,执行接口的clinit()方法不需要先执行父接口的clinit()方法.只有当父接口中的静态成员变量被使用到时才会执行父接口的clinit()方法.

虚拟机会保证在多线程环境中一个类的clinit()方法别正确地加锁,同步.当多条线程同时去初始化一个类时,只会有一个线程去执行该类的clinit()方法,其它线程都被阻塞等待,直到活动线程执行clinit()方法完毕.

其他线程虽会被阻塞,只要有一个clinit()方法执行完,其它线程唤醒后不会再进入clinit()方法。同一个类加载器下,一个类型只会初始化一次。

6 类的卸载

当代表一个类的Class对象不再被引用,那么Class对象的生命周期就结束了,对应的在方法区中的数据也会被卸载。

Jvm自带的类加载器装载的类,是不会卸载的,由用户自定义的类加载器加载的类是可以卸载的。

参考

  • 《码到成功》
  • 《深入理解Java虚拟机第三版》

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
20688 0
【Java 虚拟机原理】Java 类中的类加载初始化细节 ( 只使用类中的常量时加载类不会执行到 ‘初始化‘ 阶段 )
【Java 虚拟机原理】Java 类中的类加载初始化细节 ( 只使用类中的常量时加载类不会执行到 ‘初始化‘ 阶段 )
28 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
23577 0
JVM详解之:类的加载链接和初始化
JVM详解之:类的加载链接和初始化
16 0
JVM的类加载机制
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。  类加载的规则: 全盘负责,当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非...
738 0
+关注
javaedge
关注公众号:JavaEdge,后台回复面试,领取更多大厂求职资源。曾在百度、携程、华为等大厂搬砖,专注Java生态各种中间件原理、框架源码、微服务、中台等架构设计及落地实战,只生产硬核干货!
2303
文章
1
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载