JVM 类的加载分三步走:加载(load)-> 链接(link)-> 初始化(init)。
加载
-
通过类的全限定名获取定义该类的二进制字节码文件
咱们晓得.class 文件有几种常见的加载形式:- 本地零碎间接加载
- 网络获取
- 从 zip 压缩包中读取(jar,war 包)
- 运行时计算生成(动静代理)
- 由其余文件生成(JSP)
- 从专有数据库中提取(比拟少)
- 从加密文件中获取(避免.class 文件被反编译)
- 将这个字节流所代表的动态存储构造转化为办法区的运行时数据结构
- 在内存中生成一个代表这个类的 java.lang.Class 对象,作为办法区中这个类的各种数据的拜访入口
链接
链接也分三步走:验证—> 筹备 -> 解析
- 验证
确保 class 文件的字节流中蕴含的信息合乎以后虚拟机的要求,保障被加载类的正确性,爱护虚拟机平安。
验证次要有四种:文件格式验证,元数据验证,字节码验证,符号援用验证 -
筹备
为类变量(动态变量)分配内存(到办法区),并设置默认初始值(零值)
特例:- 这里不蕴含 final 润饰的动态变量,因为 final 变量在编译时就会分配内存空间(因为它是常量),筹备阶段会显式初始化(间接初始化为代码中赋予的值)。
- 这里也不蕴含实力变量,因为实例变量是随着对象调配到堆中的。
- 解析
将常量池中的符号援用转换为间接援用。
实际上解析操作通常在初始化之后再执行。
符号援用:一组符号来形容所援用的指标。
间接援用:间接指向指标的指针,绝对偏移量或句柄。
次要针对类或接口,字段,接口办法,办法类型等。
初始化
- 执行类结构器办法 <clinit>()的过程。
- 这个办法不须要定义,由 javac 编译器收集类中的所有动态变量的赋值动作和动态代码块中的语句合并而来(既类中动态的操作,所以叫初始化)
- 结构器办法中的指令按语句在源文件中呈现的程序执行(即从上至下,先呈现的先执行)
- 如果有父类,就先执行父类的 <clinit>()
- 在多线程环境下,一个类的 <clinit>()办法会被加锁。这是因为一个类只须要初始化一次,避免多线程环境下被屡次初始化。