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. 热点检测:通过计数器(方法调用计数器、回边计数器)识别热点代码。

相关推荐
桦说编程2 小时前
并发编程踩坑实录:这些原则,帮你少走80%的弯路
java·后端·性能优化
程序猿零零漆2 小时前
Spring之旅 - 记录学习 Spring 框架的过程和经验(十三)SpringMVC快速入门、请求处理
java·学习·spring
tkevinjd2 小时前
net1(Java中的网络编程、TCP的三次握手与四次挥手)
java
码头整点薯条2 小时前
基于Java实现的简易规则引擎(日常开发难点记录)
java·后端
Wang's Blog2 小时前
Nodejs-HardCore: 深入解析DBF文件之二进制文件处理指南
开发语言·nodejs
hoiii1872 小时前
基于LSB匹配的隐写术MATLAB实现程序
开发语言·matlab
J2虾虾2 小时前
Java使用的可以使用的脚本执行引擎
java·开发语言·脚本执行
幻云20102 小时前
Next.js指南:从入门到精通
开发语言·javascript·人工智能·python·架构
老马识途2.02 小时前
java处理接口返回的json数据步骤 包括重试处理,异常抛出,日志打印,注意事项
java·开发语言