JVM原理-类加载过程
Java类加载过程可以看作是将我们写的代码(以class文件的形式)转化为可以运行的程序。这过程分为几个步骤:加载、链接、初始化、使用和卸载。
1.加载:找到并打开书
- 过程:从书架上找到你要的书,把它拿到桌子上,准备阅读。
- 对应Java的过程:JVM从硬盘上找到编译好的class文件(如
Dog.class
),然后把它读入内存里,生成一个Class
对象。- Class对象:这个对象包含了类的各种信息,比如类名、方法、字段等,相当于一本被打开的书。
2. 链接:检查书的内容和准备好工具
链接阶段包括验证、准备和解析三个步骤:
- 验证:检查书里的内容是否有错误。
- 对应Java的过程:检查class文件的格式和内容是否合法,确保没有语法错误或其他问题。如果有错误,程序会抛出异常,无法继续运行。
- 准备:为静态变量分配内存并初始化为默认值。
- 对应Java的过程:静态变量在这个阶段被分配内存,并初始化为默认值。例如,
static int a;
会被初始化为0
。
- 对应Java的过程:静态变量在这个阶段被分配内存,并初始化为默认值。例如,
- 解析(可选):把间接引用转换为直接引用。
- 对应Java的过程:把符号引用(指向类、方法、字段的字符串)转换为直接引用(内存地址),方便后续快速访问。
3. 初始化:翻开书并开始阅读
- 过程:正式开始根据书里的内容进行设置和操作。
- 对应Java的过程:执行类的初始化,包括:
- 静态变量的赋值:将静态变量赋值为代码中定义的值。例如,
static int a = 10;
,此时a
会被设置为10
。 - 静态代码块的执行:依次执行静态代码块中的代码。静态代码块可以用于复杂的初始化工作。
- 顺序:静态变量和静态代码块的初始化按照它们在代码中出现的顺序进行。
- 静态变量的赋值:将静态变量赋值为代码中定义的值。例如,
4. 使用:开始利用书中的信息
- 过程:根据书中的指导完成具体任务。
- 对应Java的过程:实际使用类和对象,包括创建实例和调用方法。
- 对象创建:如
new Dog()
,JVM会在堆内存中为Dog
对象分配空间。- 步骤:
- 分配内存:为新对象分配内存。
- 设置默认值:对象的属性被初始化为默认值(如数字为
0
,对象引用为null
)。 - 执行初始化代码:按照代码中定义的顺序为属性赋值。
- 执行构造函数:运行构造函数,进行进一步初始化和操作。
- 步骤:
- 对象创建:如
5. 卸载:书用完后放回书架
- 过程:书看完了,把它合上,放回书架,不再需要它了。
- 对应Java的过程:当类不再需要时,JVM会把类从内存中卸载,释放占用的资源。
- 垃圾回收:JVM的垃圾回收机制会自动检测不再使用的类和对象,并回收它们占用的内存。
对类初始化的5种情况
根据JVM规范,类初始化的情况有且只有以下5种:
-
创建类的实例:当我们用
new
来创建类的对象时,比如new Dog()
,如果这个类还没有被初始化,那么它必须先初始化才能创建对象。 -
调用类的静态方法:比如
Dog.bark()
,静态方法属于类本身而不是某个对象,因此在调用时,类必须先初始化。 -
访问类或接口的静态变量:当我们访问类的静态变量时,比如
Dog.age
,这也会导致类的初始化。 -
使用
Class.forName()
方法:这种方法会强制初始化一个类。比如Class.forName("Dog")
会让Dog
类被加载并初始化。 -
类加载器启动主类:当我们运行一个程序时,比如执行
java DogMain
,JVM会先初始化DogMain
类并执行它的main
方法。
符号引用和直接引用的区别是什么?
- 符号引用:可以理解为一种间接的引用,它并不指向内存中的具体位置。相当于书里提到了某个章节,但没有告诉你具体在哪一页。
- 直接引用:是内存中的具体地址,也就是可以直接找到的目标地址。相当于书里直接标明某个章节在第几页。
类变量和实例变量
类变量(静态变量)
- 定义:使用
static
关键字修饰的变量。 - 归属:属于类本身,而不是某个特定的对象。
- 共享性:所有实例共享同一个类变量。如果一个实例修改了类变量的值,其他实例也会看到这个变化。
- 生命周期:在类加载时被初始化,直到类卸载时被回收。其存储在方法区(Method Area)。
- 访问方式:可以通过类名直接访问,也可以通过实例访问,但推荐使用类名进行访问。
实例变量
- 定义:没有使用
static
关键字修饰的变量。 - 归属:属于类的每个实例,每个实例都有自己独立的一份实例变量。
- 独立性:每个实例的实例变量相互独立,一个实例的实例变量变化不会影响其他实例的实例变量。
- 生命周期:在对象创建时被初始化,直到对象被垃圾回收时被回收。其存储在堆内存(Heap Memory)。
- 访问方式:只能通过对象实例来访问,不能通过类名直接访问。