虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。下面来总结梳理类加载的五个阶段。

类加载发生在程序运行期间,会有一些性能开销,但是会提供灵活性,Java动态扩展的特性就是依赖运行时期动态加载和动态连接特点

类加载分为五个阶段:

  1. 加载
  2. 验证
  3. 准备
  4. 解析
  5. 初始化

后四个阶段统称为“连接”阶段

加载

加载阶段,虚拟机完成以下三件事:

  • 通过一个类的全限定名来获取此类的二进制字节流(即class文件);
  • 将字节流的静态存储结构转化为方法区的运行时数据结构;
  • 内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问接口;

注意

  1. 虚拟机规范中未规定方法区中的运行时数据结构,由虚拟机自行定义;
  2. 实例化的java.lang.Class对象未明确规定存储在堆中,HotSpot中的class对象存在方法区中

加载阶段尚未完成时,后续连接解读那可能已经开始

验证

验证阶段主要是为了确保class文件字节流中的信息符合虚拟机要求,不会危害虚拟机自身安全。主要有以下几种格式验证:

  • 文件格式验证:验证是否符合Class文件规范
  • 元数据验证:字节码语义分析,是否符合Java语言要求
  • 字节码验证:验证类的方法体,保证运行不会产生危害
  • 符号引用验证:验证符号引用是否能找到对应的类,是否具有访问权限等

对于反复使用和验证过的代码,可以使用-Xverify:none 可以关闭类验证措施,以缩短类加载时间。因为平时开发中idea和eclipse等工具的验证功能很完善,能保证代码准确。

准备

为static变量分配空间,不包括实例变量,实例变量会在对象实例化时和对象一起分配在堆中,

  • static分配空间在“准备”阶段(在方法区中分配),赋值在“初始化”阶段。例如 public static int value = 123 会在准备阶段设置初始值0,在初始化阶段赋值“123”;
  • static+final修饰基本数据类型和字符串常量时,分配空间和赋值都是在准备阶段,可以借此优化代码
  • static+final修饰引用类型,即用new初始化时,是在构造方法中分配空间和赋值,即在初始化阶段完成

解析

将常量池中的符号引用解析为直接引用。那么问题来了,什么是符号引用和直接引用:

  • 符号引用:描述目标的符号,与虚拟机内存布局无关,以字面量的形式明确定义在class文件中。Java虚拟机内存布局各不相同,但是符号引用必须一致。Java类编译时,不知道引用的类的实际地址,因此使用符号引用。
  • 直接引用:直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。

解析动作针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。

初始化:

常说的“类加载时机”与“初始化时机”一一对应。因为要进入初始化阶段,就必须要完成加载、验证、准备、解析等阶段。

初始化发生时机如下:

  • main方法所在类总是会被首先初始化
  • 首次访问这个类的静态变量或静态方法时会导致初始化
  • 子类初始化会联动父类初始化
  • 通过子类访问父类静态变量,只会触发父类初始化
  • Class.forName
  • new会导致初始化

以下情况不会导致类初始化的情况:

  • static final不会触发初始化
  • 访问类对象.class
  • 创建该类的数组
  • 类加载器的loadClass方法
  • Class.forName的参数2为false

初始化阶段实际上是执行类构造器<clinit>()方法的过程。<clinit>()会根据源文件中顺序收集类中所有类变量赋值动作和static块语句

注意:static块能访问代码块之前的变量,之后的变量可以赋值,但不能访问

<clinit>()方法的执行有如下特点:

  • 第一个执行clinit方法的一定是Object类
  • 父类中静态语句块一定优先于子类
  • 如果类中没有static方法和变量,不会产生clinit方法
  • 接口中如果有static变量,也会生成clinit,但是子接口不会执行父接口中的clinit,接口的实现类也不会调用接口中的clinit
  • 多线程时,只会有一个线程会去执行clinit,会加锁,保证线程安全

【JVM】类加载时机与过程的更多相关文章

  1. 从一道面试题来认识java类加载时机与过程

    说明:本文的内容是看了<深入理解Java虚拟机:JVM高级特性与最佳实践>后为加印象和理解,便记录了重要的内容. 1  开门见山 以前曾经看到过一个java的面试题,当时觉得此题很简单,可 ...

  2. java类加载时机与过程

    转自:http://www.tuicool.com/articles/QZnENv 说明:本文的内容是看了<深入理解Java虚拟机:JVM高级特性与最佳实践>后为加印象和理解,便记录了重要 ...

  3. 从一道面试题来认识java类加载时机与过程【转】

    说明:本文的内容是看了<深入理解Java虚拟机:JVM高级特性与最佳实践>后为加印象和理解,便记录了重要的内容. 1  开门见山 以前曾经看到过一个java的面试题,当时觉得此题很简单,可 ...

  4. 【转载】java类加载时机与过程

    1  开门见山 以前曾经看到过一个java的面试题,当时觉得此题很简单,可是自己把代码运行起来,可是结果并不是自己想象的那样.题目如下: class SingleTon { private stati ...

  5. 【深入Java虚拟机】一 JVM类加载过程

    首先Throws(抛出)几个自己学习过程中一直疑惑的问题: 1.什么是类加载?什么时候进行类加载? 2.什么是类初始化?什么时候进行类初始化? 3.什么时候会为变量分配内存? 4.什么时候会为变量赋默 ...

  6. JVM类加载过程学习总结

    JVM类加载过程学习总结 先不说JVM类加载的原理,先看实例: NormalTest类,包含了一个静态代码块,执行的任务就是打印一句话. /** * 在正常类加载条件下,看静态代码块是否会执行 * @ ...

  7. (二十七)JVM类加载器机制与类加载过程

    一.Java虚拟机启动.加载类过程分析 下面我将定义一个非常简单的java程序并运行它,来逐步分析java虚拟机启动的过程. package org.luanlouis.jvm.load; impor ...

  8. JVM类加载过程详细分析

    双亲委派加载模型 为什么需要双亲委派加载模型 主要是为了安全,避免用户恶意加载破坏JVM正常运行的字节码文件,比如说加载一个自己写的java.util.HashMap.class.这样就有可能造成包冲 ...

  9. JVM -- 类加载的过程

    类的加载过程? 一个Java文件从编码完成到最终执行,一般主要包括"编译"和"运行"两个过程.编译,即把我们写好的java文件,通过javac命令编译成字节码, ...

随机推荐

  1. 【PUPPETEER】初探之元素获取(二)

    一.涉及的知识点 如何使用css selector 常用元素获取 $ 元素选择 type (api 输入) click (api 点击) 二.学习网址 https://github.com/Googl ...

  2. 记录一下Comparator的用法

    Collections.sort(res, new Comparator<ArrayList<Integer>>() {             @Override       ...

  3. Java 滴IO系统

    JAVA IO 流可以概括为 "两个对应,一个桥梁".两个对应指字节流(Byte Stream)和字符流(Char Stream)的对应,输入流和输出流的对应. 一个桥梁指从字节流 ...

  4. 接上一篇:(二) IOC的概念和作用

    IOC的概念和作用 控制反转(IoC:Inversion of Control)把创建对象的权利转交给框架(框架的重要特征),并非面向对象的专用术语. 它包含依赖注入(DI:Dependency In ...

  5. influxdb的基本使用

    influxDB名词 database:数据库: measurement:数据库中的表: points:表里面的一行数据. influxDB中独有的一些概念 Point由时间戳(time).数据(fi ...

  6. linux下iptables原理

    原文链接:https://www.cnblogs.com/ggjucheng/archive/2012/08/19/2646466.html iptables简介 netfilter/iptables ...

  7. JavaScript使用中的一些小技巧

    任何一门技术在实际中都会有一些属于自己的小技巧.同样的,在使用JavaScript时也有一些自己的小技巧,只不过很多时候有可能容易被大家忽略.而在互联网上,时不时的有很多同行朋友会总结(或收集)一些这 ...

  8. C++编程指南续(4-5)

    五.常量 常量是一种标识符,它的值在运行期间恒定不变.C语言用 #define来定义常量(称为宏常量).C++ 语言除了 #define外还可以用const来定义常量(称为const常量). 5.1  ...

  9. PyQt(Python+Qt)学习随笔:什么是信号绑定(Unbound and Bound Signals)?

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 1.概述 信号的绑定是由在类的实例变量中第一次通过类实例的方式(即"self.信号&quo ...

  10. PyQt学习遇到的问题:重写notify发送的消息为什么首先给了一个QWindow对象?

    在PyQt开发图形界面应用时,从QApplication派生的子类重写notify方法后(具体请参考<PyQt学习随笔:通过自定义类重写QApplication的notify方法捕获应用的所有消 ...