JVM和类
调用java命令运行某个java程序时,该命令会启动一个java虚拟机进程,这个JVM进程可以有多个綫程。
同一个JVM的所有綫程、所有变量都处于同一个进程里,它们都使用该JVM进程的内存区。以下状况,JVM进程被终止:
- 1. 程序运行到最后,正常结束
- 2. 程序运行到使用System.exit()或Runtime.getRuntime().exit()代码处,结束程序
- 3. 程序执行过程中遇到未捕获的异常或错误而结束
- 4. 程序所在平台强制结束了JVM进程
Java程序结束时,JVM进程结束,对应的内存中的数据/状态,就会丢失。
类的加载
程序主动使用某个类时,如果该类没有加载到内存,那麽,系统会通过加载、连接、初始化三个步骤来初始化这个类。通常,没什麽意外,JVM会连续完成这三个步骤,所有,这三个步骤有时统称,类加载,或类的初始化。
到这裡,我们得有个概念,类的加载分3个步骤。
加载
将类的字节码文件,就是class文件,读入内存,并为之创建一个java.lang.Class对象。
即,加载过程,有两步,读class文件到内存,创建java.lang.class对象。
类的加载,由类加载器ClassLoader完成,通常由JVM提供。
类加载器ClassLoader
类加载器,分为四个层次,根类加载器、扩展加载器、JVM提供的加载器(被称谓系统加载器)、开发者继承ClassLoader类来创建的自定义类加载器
- Boostroop ClassLoader:根类加载器
- Extension ClassLoader:扩展类加载器
- System ClassLoader:系统类加载器
- 用户类加载器
以后再开文章细说类加载器
使用不同的类加载器,可以从不同来源,加载类的二进制数据,通常有以下来源:
- 本地文件系统加载class文件
- 从JAR包中直接加载class文件
- 从网络加载class文件
- 动态编译一个Java源文件,并执行加载
连接
前面说了加载,这裡谈谈第二个步骤,连接。类被加载后,系统为之生成对应的class文件,接著进入连接阶段。
连接,负责把类的二进制数据合併到JRE中,分为三个阶段:
- 验证:检验被加载的类,是否有正确的内部结构,并和其他类协调一致
- 准备:为类的类变量分配内存,并设置默认初始值
- 解析:将类的二进制数据中的符号引用,替换成直接引用
初始化
第三个步骤,类的初始化阶段,JVM负责初始化类,主要是对类变量初始化。
Java类中,对类变量指定初始值有2种方式:
- 声明时指定
- 使用静态初始化块,为类变量指定初始值
JVM初始化一个类包含如下步骤:
- 如果类还没有加载和连接,程序先加载和连接
- 如果该类的直接父类还没有初始化,先初始化直接父类
- 如果类中有初始化语句(赋值、静态代码块),则系统一次执行这些语句
总结
总结一下,类的加载分三个步骤,由JVM连续
- 加载
- 将class文件读入内存
- 创建对应的java.long.Class对象
- 连接
- 验证类内部结构,是否与它类协调
- 准备,为类变量分配内存,设初始化值
- 解析,符号引用替换成直接引用
- 初始化
- 类变量初始化