在 java 代码中,类的加载、连接与初始化过程都是在程序运行期间完成的。
提供了更大的灵活性,增加了更多的可能性,在一阵烟雾弥漫后,大力神 JVM 将 class 加载到内存来完成强大的功能。
而且在 JVM 中对有些规范并没有给出严格要求,只是进行一些描述,厂商可以根据自己需要,来实现适合自己的 JVM,阿里巴巴就自己的 jvm
Java虚拟机与程序的生命周期
在如下几种情况下,Java虚拟机将结束生命周期
- 执行了
System.exit()
方法 - 程序正常执行结束
- 程序在执行过程中遇到了异常或错误而异常终止
- 由于操作系统出现错误导致 java 虚拟机进程终止
load-states.png
1. 加载
查找并加载类的二进制数据
2. 连接
验证
确保被加载的类的正确性
准备
为类的静态变量分配内存,并将其初始化为默认值
解析
把类中的符号引用转换为直接引用
3. 解析
把类的静态变量赋予正确的初始值
java 程序对类的使用方式分为两种
- 主动使用
- 创建类的实例
- 访问或对类或接口的静态变量赋值,访问以及赋值的助记符getstatic和setstatic
- 调用静态方法,对应的助记符invokestatic
- 反射
- 初始化一个类的子类
- JVM 启动时表明为启动类的类
- JDK 1.7 开始提供的动态语言支持
- 被动使用
所有的 Java 虚拟机实现必须在每一个类或接口被 Java 程序首次主动使用时才初始化他们
类的加载
类的加载将类的.class 文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在内存中创建一个 java.lang.class 对象来封装类在方法区的数据结构。
加载.class 文件的方式
- 从本地系统中直接加载
- 网络下载 class 文件
- 从 zip, jar 等归档文件中加载 .class 文件
- 从专有数据库提取.class文件
- 将java 源文件动态编译为.class文件
-XX: +<option> 表示开启 option 选项
-XX: -<option> 表示关闭 option 选项
-XX:<option>=<value> 将 option 选项值设置为 value
class Parent {
public static String parentStr = "parent static";
static {
System.out.println("parent static block");
}
}
class Child extends Parent{
public static String childStr = "child static";
static {
System.out.println("child static block");
}
}
public class ZiClient {
public static void main(String[] args) {
System.out.println("hello jvm");
System.out.println(Child.parentStr);
}
}
hello jvm
parent static block
parent static
从输出上来看这里并没有初始化 Child ,只是初始化 Parent 因为打印了 Parent static 代码块内容
屏幕快照 2019-04-06 下午4.56.35.png
可以通过开启 TraceClassLoading 选项来查看 JVM 加载类的过程。
hello jvm
[0.556s][info ][class,load] source: jrt:/java.base
[0.556s][info ][class,load] source: jrt:/java.base
[0.556s][info ][class,load] source: jrt:/java.base
[0.557s][info ][class,load] source: jrt:/java.base
[0.557s][info ][class,load] source: jrt:/java.base
[0.557s][info ][class,load] com.zidea.jvm.demo.Parent source: file:/Users/jangwoo/IdeaProjects/learnjvm3/target/classes/
[0.557s][info ][class,load] com.zidea.jvm.demo.Child source: file:/Users/jangwoo/IdeaProjects/learnjvm3/target/classes/
parent static block
parent static
然后我们就可以查看到了虽然没有初始化 Child 类,但是已经将这个 Child 进行了加载。
class Parent {
public static String final parentStr = "parent static";
static {
System.out.println("parent static block");
}
}
我们将 parentStr 变为 final 类型后再次运行应用得到下面结果,具体原因请看下次分解...
hello jvm
parent static