运行一个Java程序的时候,创一个Java虚拟机进程,在这个进程包含了该Java程序的所有线程,无论该程序是简单的单线程还是复杂的多线程。
同一个JVM的所有线程、所有变量都处于一个进程中,使用的是JVM进程的内存区域。
JVM进程被终止的原因:
- 程序正常执行到,最后结束
- 在程序运行中使用到了System.exit() 或 Runtime.getRuntime.exit()的代码来结束程序
- 遇到了未捕获的异常或者错误而结束
- 程序所在平台强制结束了JVM进程
1、类的加载
当我们的程序代码中使用到了某个类时,如果该类未被加载到内存在,系统会通过加载、连接、初始化三个步骤来对该类进行初始化 ,这统称为类的加载或类的初始化
类的加载: 将类的class文件读取到内存中,并创建一个java.lang.Class的对象
,也就是说是所有在程序中用到的类,在系统中都有对应的一个Class的实例对象。
类加载器:用于加载类的,通常是有JVM提供,比较常见的是系统类加载器,我们也可以自定义类来继承ClassLoader基类创建自己的类加载器。
2、类的连接
在类被加载,创建对应的Class对象后,就进入了连接阶段,该阶段会负责把二进制文件合并到JRE中。
类的连接有可分为下面三个阶段:
验证
:用于校验被加载的类是否有正确的内部结构,并和他类协调一致准备
:类准备派人负责为类的静态属性分配内存,并设置默认初始化值解析
:将类的二进制数据中的符号引用替换为直接引用
3、类的初始化
当类被加载后,虚拟机主要负责对类的静态属性进行初始化,初始化的方式主要分两种:
- 声明静态属性时指定初始化值;
- 使用静态初始化块为静态属性指定初始值
public class Test{
// 情况1:声明静态属性时指定初始化值;
static int a = 5 ;
// 情况2:使用静态初始化块为静态属性指定初始值
static String b;
static double c;
static int d;
static {
b = "hello";
c = 3.0;
}
}
在上面的代码中为静态变量a、b、c
显示的指定了初始值
,但是没有指定静态变量 d
的值,系统默认会将 d 设置为默认初始值 0
。
JVM初始化一个类的步骤:
- 在使用的到该类的时候,如果该类未被加载,JVM会先加载连接该类
- 如果该类的直接父类没有被初始化,就会初始化直接父类
- 如果类中有初始化语句(静态变量和代码块),JVM会依次执行这些初始化语句。
从上面的步骤来看,初始一个类的时候,会去初始它的直接父类,我们知道Object的所有的类的父类,所以每次加载的时候首先加载的是java.lang.Object类。
我们所写代码中有会促使JVM去加载初始化一某个类或者接口的情况:
- 创建类的实例的时候,通过
new关键词
、反射
或者反序列化
的时候 - 使用用某个
类的静态变量、静态方法
的时候 - 通过反射来强制创建某个类的或者接口对应的
java.lang.Class对象
,例如:Class.forName(“java.lang.String”); 初始化某个类的子类的时候,其父类都会被加载
。- 直接使用
java.exe
命令来运行某个主类,程序会先初始化该主类
。