JVM 深度理解 —— 程序的底层运行逻辑

目录

[一:JVM 内存全景图 (Runtime Data Areas)](#一:JVM 内存全景图 (Runtime Data Areas))

[1.1 线程私有区(生命周期与线程同步)](#1.1 线程私有区(生命周期与线程同步))

[1.2 线程共享区(内存分配主体)](#1.2 线程共享区(内存分配主体))

二:类加载系统 (Class Loader Subsystem)

[2.1 加载的五个阶段](#2.1 加载的五个阶段)

[2.2 双亲委派模型](#2.2 双亲委派模型)

三:垃圾回收深度解析 (Garbage Collection)

[3.1 对象存活判定](#3.1 对象存活判定)

[3.2 垃圾回收算法](#3.2 垃圾回收算法)

[3.3 经典垃圾收集器](#3.3 经典垃圾收集器)

[四:JVM 参数与性能调优](#四:JVM 参数与性能调优)

[4.1 常用诊断命令](#4.1 常用诊断命令)

[4.2 示例代码:通过代码感受 JVM 限制](#4.2 示例代码:通过代码感受 JVM 限制)

[五:执行引擎与 JIT 编译](#五:执行引擎与 JIT 编译)


一:JVM 内存全景图 (Runtime Data Areas)

JVM 内存不只是"堆和栈",它是一个精密设计的协作系统。

1.1 线程私有区(生命周期与线程同步)

  • 程序计数器 (PC Register)

    • 原理:当前线程执行的字节码行号指示器。

    • 特点 :占用内存极小,是 JVM 规范中唯一没有规定 OutOfMemoryError 的区域。

  • 虚拟机栈 (JVM Stack)

    • 组成 :由一个个栈帧 (Stack Frame) 组成。每个方法从调用到执行完成,对应一个栈帧在栈中入栈到出栈的过程。

    • 栈帧内容:局部变量表(存基本类型和引用指针)、操作数栈(计算中间结果)、动态链接、方法出口。

    • 异常StackOverflowError(递归太深)或 OutOfMemoryError(线程太多)。

  • 本地方法栈 (Native Method Stack)

    • 为虚拟机使用到的 Native 方法(如 C/C++ 编写的底层库)服务。

1.2 线程共享区(内存分配主体)

  • 堆 (Heap)

    • 分代结构 :分为新生代 (Young Gen)老年代 (Old Gen)

    • 新生代细分:Eden 区、Survivor 0 区、Survivor 1 区(比例通常为 8:1:1)。

    • 功能:几乎所有的对象和数组都在此分配空间。

  • 元空间 (Metaspace)

    • 位置 :Java 8 之后,从 JVM 内部移到了本地内存

    • 内容:类信息、常量池、静态变量、JIT 编译后的代码。


二:类加载系统 (Class Loader Subsystem)

类加载负责将 .class 文件转换成内存中的 Class 对象。

2.1 加载的五个阶段

  1. 加载:读取二进制流。

  2. 验证:格式、元数据、字节码、符号引用验证。

  3. 准备 :为 static 变量分配内存并初始化为零值 (注意:此时不执行逻辑,static int a = 123 此时 a 是 0)。

  4. 解析:将常量池内的符号引用替换为直接引用。

  5. 初始化 :执行 <clinit>() 方法(合并所有静态代码块和静态变量赋值语句)。

2.2 双亲委派模型

  • 自底向上检查:先看自己加载过没,没加载过问父类,一直问到启动类加载器。

  • 自顶向下加载:父类无法加载时,子类才尝试加载。

  • 核心逻辑示例

    java 复制代码
    // 伪代码演示加载逻辑
    protected Class<?> loadClass(String name) {
        // 1. 先查缓存
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            // 2. 委派给父类
            if (parent != null) c = parent.loadClass(name);
            else c = findBootstrapClassOrNull(name);
        }
        // 3. 实在不行再自己动手
        if (c == null) c = findClass(name);
        return c;
    }

三:垃圾回收深度解析 (Garbage Collection)

3.1 对象存活判定

  • 引用计数法:容易导致循环引用(Java 不采用)。

  • 可达性分析 :从 GC Roots(如栈中引用、静态变量、常量池等)出发,无法到达的对象即为垃圾。

3.2 垃圾回收算法

  • 标记-复制:新生代使用。内存利用率 90%,不会有内存碎片。

  • 标记-清除:老年代使用。会产生碎片。

  • 标记-整理:老年代使用。移动存活对象,解决碎片问题,但效率稍低。

3.3 经典垃圾收集器

  • CMS (Concurrent Mark Sweep):追求最短停顿,老年代收集器,并发执行。

  • G1 (Garbage First):将内存切成一个个 Region。不仅回收老年代,也回收新生代,是目前 JDK 8+ 的主流选择。

  • ZGC:Java 11+ 引入,停顿时间不随堆大小增加,始终控制在 10ms 以内。


四:JVM 参数与性能调优

4.1 常用诊断命令

  • jps:查看 Java 进程。

  • jstat -gc <pid> 1000:每秒查看一次 GC 统计。

  • jstack <pid>:查看线程快照(解决死锁、CPU 飙升)。

  • jmap -dump:format=b,file=heap.hprof <pid>:导出堆快照。

4.2 示例代码:通过代码感受 JVM 限制

场景:模拟频繁 Full GC 导致的程序卡顿

java 复制代码
import java.util.LinkedList;
import java.util.List;

/**
 * 设置 JVM 参数: -Xms100m -Xmx100m -XX:+PrintGCDetails
 */
public class FullGCDemo {
    private static List<Object> cache = new LinkedList<>();

    public static void main(String[] args) throws InterruptedException {
        System.out.println("程序启动,开始向堆内存注入数据...");
        
        while (true) {
            // 模拟业务逻辑不断产生无法回收的对象(内存泄漏)
            for (int i = 0; i < 1000; i++) {
                cache.add(new byte[1024]); // 每次加 1KB
            }
            
            // 适当移除一些,但总体增长,导致老年代不断填满触发 Full GC
            if (cache.size() > 50000) {
                for (int i = 0; i < 10000; i++) {
                    cache.remove(0);
                }
            }
            
            Thread.sleep(10); // 减慢速度,便于观察控制台 GC 日志
        }
    }
}

五:执行引擎与 JIT 编译

JVM 并不是单纯的解释执行:

  1. 解释器:程序启动时立即执行,省去编译时间。

  2. JIT 编译器 (Just-In-Time):将经常执行的"热点代码"编译成机器码,提升性能。

  3. 热点检测:通过计数器(方法调用计数器、回边计数器)识别热点代码。

相关推荐
Codiggerworld5 分钟前
JVM内存模型——你的对象住在哪里?
jvm
市场部需要一个软件开发岗位5 分钟前
JAVA开发常见安全问题:纵向越权
java·数据库·安全
历程里程碑18 分钟前
普通数组----合并区间
java·数据结构·python·算法·leetcode·职场和发展·tornado
执风挽^35 分钟前
Python基础编程题2
开发语言·python·算法·visual studio code
程序员泠零澪回家种桔子37 分钟前
Spring AI框架全方位详解
java·人工智能·后端·spring·ai·架构
Z9fish1 小时前
sse哈工大C语言编程练习20
c语言·开发语言·算法
CodeCaptain1 小时前
nacos-2.3.2-OEM与nacos3.1.x的差异分析
java·经验分享·nacos·springcloud
萧鼎1 小时前
Python 包管理的“超音速”革命:全面上手 uv 工具链
开发语言·python·uv
Anastasiozzzz2 小时前
Java Lambda 揭秘:从匿名内部类到底层原理的深度解析
java·开发语言
骇客野人2 小时前
通过脚本推送Docker镜像
java·docker·容器