内存区域

01已经讲过了,不做赘述。

JVM 的内存区域:

  • 程序计数器:工序卡指针
  • 虚拟机栈:当前工单现场
  • 本地方法栈:外协工位
  • 堆:大仓库
  • 方法区:档案室

所以“内存区域”本质上就是:

JVM 把内存切成几块,每块负责不同工作。

为什么要分区?

因为程序运行时有不同类型的数据:

  • 有的是当前方法临时用的
  • 有的是对象
  • 有的是类的说明书
  • 有的是下一条要执行的位置

这些东西生命周期、大小、用途都不同,所以 JVM 要分开管理。

线程私有

  • 程序计数器
  • 虚拟机栈
  • 本地方法栈

线程共享

  • 方法区

类加载机制

类加载机制 就是:

一个 Java 类,从磁盘上的 .class 文件,变成 JVM 里可以使用的类,到底经历了什么过程。

01已经学了“元空间放类信息”,也就是工厂建好了,需要开始运行,但是我怎么知道需要怎么工作?

这些类信息是怎么进去的?

这就是类加载机制。

  • .class 文件 = 外面送来的产品图纸
  • JVM = 工厂
  • 类加载器 = 把图纸搬进工厂的人
  • 元空间 = 档案室
  • 类加载机制 = 图纸进厂、审核、入档、能正式使用的全过程

1.类加载大致分 5 步

最常见的说法是:

  1. 加载
  2. 验证
  3. 准备
  4. 解析
  5. 初始化
1. 加载

把图纸搬进厂里

把 .class 文件读进 JVM。

比如你写了:

User u = new User();

JVM 先得找到 User.class,把它读进来。

这一阶段主要做三件事:

  • 通过类的全限定名找到 .class
  • 读入二进制字节流
  • 在方法区/元空间里生成这个类的运行时结构


2. 验证

审核图纸是不是伪造的、是不是乱写的

检查这个类文件靠不靠谱,会不会危害 JVM。

比如检查:

  • 文件格式对不对
  • 字节码是否合法
  • 语义是否合理

3. 准备

预留静态档案(static )位置

给 类变量(static变量) 分配内存,并赋默认初始值。

比如:

class Demo { static int a = 10; }

在“准备”阶段,先做的不是立刻赋值 10,而是:

  • 给 a 分配空间
  • 先给默认值 0

准备阶段:static 变量先拿默认值,不是程序员写的值


4. 解析

把图纸上的“去 A 车间”换成具体门牌号

把常量池里的“符号引用”替换成“直接引用”。

比如类里写了:

  • 调用哪个方法
  • 用哪个类
  • 访问哪个字段

一开始很多只是“名字”,解析阶段会把这些名字变成 JVM 真正能定位到的目标。


5. 初始化

图纸正式生效,工厂开始按这份规则运行

真正执行类里的静态赋值和静态代码块。

比如:

class Demo { static int a = 10; static { System.out.println("初始化 Demo"); } }

到了初始化阶段,才真正执行:

  • a = 10
  • static { ... }

2.一个最简单的例子

class Demo {
    static int a = 10;

    static {
        System.out.println("Demo 初始化");
    }
}

public class Test {
    public static void main(String[] args) {
        System.out.println(Demo.a);
    }
}

执行时大致是:

  1. JVM 发现要用 Demo
  2. 加载 Demo.class
  3. 验证它是否合法
  4. 准备:给 a 分配空间,先设默认值 0
  5. 解析:处理符号引用
  6. 初始化:
    • 执行 static int a = 10
    • 执行 static { System.out.println("Demo 初始化"); }
  7. 最后输出 10

3.类加载器是什么

类不是 JVM 自己凭空拿进来的,要有人去“搬”。

这个“搬运工”就是 类加载器

常见有 3 个:

  1. Bootstrap ClassLoader
    加载核心类库,比如 java.lang.String

  2. Extension / Platform ClassLoader
    加载扩展类库

  3. Application ClassLoader
    加载你自己写的应用类

可以理解成:

  • 启动类加载器:总厂官方搬运队
  • 扩展类加载器:分厂公共搬运队
  • 应用类加载器:你自己项目的搬运队

4.双亲委派是什么

新图纸先问总厂档案室有没有,能用总厂的就不用你自己乱造。

一个类加载器收到加载请求后,不会先自己干,而是先往上问父加载器。父加载器能加载,就让父加载器加载;父加载器不行,自己再加载。

为什么这么做?

主要两个目的:

  1. 避免重复加载
  2. 保证核心类库安全

比如你自己写一个:

package java.lang;
 public class String { 
}

按正常机制,它不会随便替换 JVM 自带的 String。

这就是双亲委派的重要意义。


对象创建过程

Product p = new Product();

从01可以知道new Product() 创建出来的,就是一个 对象,是在堆里面的。

而这个讲的就是其创建过程

对应过程:

  1. 检查 Product 类是否已加载
  2. 在堆里分配对象内存
  3. 实例字段先设默认值
  4. 写入对象头
  5. 执行构造方法
  6. 把引用赋给 p

这是java的对象,

  1. 先确认 Product (图纸)已经入档
  2. 仓库(堆)里腾一个位置
  3. 先放空白默认货物
  4. 贴上产品标签
  5. 按构造规则完成初始装配
  6. 把货位编号 p交给工人

设置对象头

JVM 还会给对象写一些额外信息,比如:

  • 这个对象属于哪个类
  • 哈希码相关信息
  • 锁相关信息

这些放在对象头里。

你现在先把它理解成:

给货物贴上标签:这是谁家的、什么类型。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐