完整版JVM 深度学习体系(二)

🔥 JVM 全体系深度解析

第一部分:JVM 核心架构

1.1 JVM 整体架构图

java 复制代码
┌─────────────────────────────────────────────────────────────────┐
│                         JVM 虚拟机                               │
│                                                                 │
│  ┌───────────────┐    ┌─────────────────────────────────────┐  │
│  │  类加载子系统   │───→│           运行时数据区               │  │
│  │  (ClassLoader)│    │  ┌─────────────────────────────┐    │  │
│  │               │    │  │ 线程私有                     │    │  │
│  │  · 加载       │    │  │  ├─ 程序计数器 (PC)          │    │  │
│  │  · 验证       │    │  │  ├─ 虚拟机栈 (VM Stack)      │    │  │
│  │  · 准备       │    │  │  └─ 本地方法栈 (Native Stack) │    │  │
│  │  · 解析       │    │  ├─────────────────────────────┤    │  │
│  │  · 初始化     │    │  │ 线程共享                     │    │  │
│  └───────────────┘    │  │  ├─ 堆 (Heap)               │    │  │
│          │             │  │  └─ 方法区 (Method Area)     │    │  │
│          ↓             │  └─────────────────────────────┘    │  │
│  ┌───────────────┐    └─────────────────────────────────────┘  │
│  │   执行引擎    │                        ↑                    │
│  │               │                        │                     │
│  │  ┌──────────┐ │    ┌─────────────────┴──────────────────┐    │
│  │  │ 解释器   │ │───→│        本地方法接口 (JNI)        │    │
│  │  └──────────┘ │    └─────────────────┬──────────────────┘    │
│  │  ┌──────────┐ │                      ↓                       │
│  │  │ JIT编译器│ │    ┌─────────────────────────────────┐      │
│  │  └──────────┘ │    │         本地方法库               │      │
│  │  ┌──────────┐ │    │  (C/C++ 编译的本地方法实现)     │      │
│  │  │  GC回收器│ │    └─────────────────────────────────┘      │
│  │  └──────────┘ │                                             │
│  └───────────────┘                                             │
└─────────────────────────────────────────────────────────────────┘

1.2 各模块职责

模块 核心职责 关键组件
类加载子系统 加载 .class 文件到 JVM ClassLoader、Class 文件解析器
运行时数据区 存储运行时数据 堆、栈、方法区、PC寄存器
执行引擎 执行字节码指令 解释器、JIT编译器、GC
本地方法接口 连接 Java 与本地代码 JNI、NATIVE 方法
本地方法库 提供本地方法实现 C/C++ 编写的底层库

第二部分:运行时数据区(Memory Structure)

2.1 运行时数据区全景图

java 复制代码
┌────────────────────────────────────────────────────────────────┐
│                      JVM 进程内存                               │
│                                                                │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │                      堆 (Heap) - 线程共享                 │  │
│  │  ┌────────────────────────────────────────────────────┐  │  │
│  │  │              老年代 (Old Generation)                │  │  │
│  │  │  ┌─────────────┐  ┌─────────────┐  ┌───────────┐  │  │  │
│  │  │  │ 大对象区    │  │ 长期存活    │  │ 字符串    │  │  │  │
│  │  │  │ (直接进入)  │  │ 对象        │  │ 常量池    │  │  │  │
│  │  │  └─────────────┘  └─────────────┘  └───────────┘  │  │  │
│  │  ├────────────────────────────────────────────────────┤  │  │
│  │  │              新生代 (Young Generation)              │  │  │
│  │  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌───────┐ │  │  │
│  │  │  │ Eden区  │  │ S0区    │  │ S1区    │  │ ...   │ │  │  │
│  │  │  │ (8/10)  │  │ From    │  │ To      │  │       │ │  │  │
│  │  │  └─────────┘  └─────────┘  └─────────┘  └───────┘ │  │  │
│  │  └────────────────────────────────────────────────────┘  │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │                      方法区 - 线程共享                    │  │
│  │  ┌──────────────┐  ┌──────────────┐  ┌───────────────┐  │  │
│  │  │ 类信息       │  │ 运行时常量池  │  │ 静态变量      │  │  │
│  │  │ (类元数据)   │  │              │  │              │  │  │
│  │  └──────────────┘  └──────────────┘  └───────────────┘  │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │                      直接内存 - 线程共享                   │  │
│  │  ┌────────────────────────────────────────────────────┐  │  │
│  │  │         NIO 直接缓冲区 (DirectByteBuffer)           │  │  │
│  │  └────────────────────────────────────────────────────┘  │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │              虚拟机栈 (VM Stack) - 线程私有              │  │
│  │  ┌────────┐  ┌────────┐  ┌────────┐                     │  │
│  │  │栈帧1   │  │栈帧2   │  │栈帧3   │  ...              │  │
│  │  │method1 │  │method2 │  │method3 │                     │  │
│  │  └────────┘  └────────┘  └────────┘                     │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │              本地方法栈 (Native Stack) - 线程私有         │  │
│  │  ┌────────┐  ┌────────┐                                  │  │
│  │  │native │  │native │  ...                             │  │
│  │  │frame1 │  │frame2 │                                  │  │
│  │  └────────┘  └────────┘                                  │  │
│  └──────────────────────────────────────────────────────────┘  │
│                                                                │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │              程序计数器 (PC Register) - 线程私有          │  │
│  │  ┌────────┐  ┌────────┐  ┌────────┐                     │  │
│  │  │ Thread1│  │ Thread2│  │ Thread3│  ...              │  │
│  │  │ PC=156 │  │ PC=89  │  │ PC=203 │                     │  │
│  │  └────────┘  └────────┘  └────────┘                     │  │
│  └──────────────────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────────────────┘

2.2 线程私有区域详解

2.2.1 程序计数器(Program Counter Register)
java 复制代码
/**
 * 程序计数器是最小的一块内存
 * 每个线程都有自己的 PC 寄存器,互不影响
 */
public class PCRegisterDemo {
    public static void main(String[] args) {
        int a = 1;      // PC = 0
        int b = 2;      // PC = 1
        int c = a + b;  // PC = 2
        System.out.println(c);  // PC = 3
    }
}

特点

  • 当前线程执行的字节码行号指示器
  • 分支、循环、跳转、异常处理都依赖它
  • 执行 Native 方法时,PC 值为 undefined
  • 唯一不会发生 OutOfMemoryError 的区域
2.2.2 虚拟机栈(VM Stack)
java 复制代码
/**
 * 虚拟机栈存储方法调用信息
 * 每个方法调用创建一个栈帧(Stack Frame)
 */
public class StackFrameDemo {
    
    public void methodA() {
        int x = 10;              // 栈帧A的局部变量
        methodB(x);               // 调用methodB
    }
    
    public void methodB(int x) { // 栈帧B的局部变量
        long y = 20L;            // 栈帧B的局部变量
        methodC();                // 调用methodC
    }
    
    public void methodC() {      // 栈帧C
        // ...业务逻辑
    }
}

栈帧结构

java 复制代码
┌─────────────────────────────────┐
│           栈帧 (Stack Frame)    │
├─────────────────────────────────┤
│  局部变量表 (Local Variables)   │
│  - 方法参数                     │
│  - 方法内局部变量               │
│  - this 引用(实例方法)        │
├─────────────────────────────────┤
│  操作数栈 (Operand Stack)       │
│  - 临时操作区域                 │
│  - 入栈/出栈操作                │
├─────────────────────────────────┤
│  动态链接 (Dynamic Linking)     │
│  - 符号引用→直接引用            │
├─────────────────────────────────┤
│  方法返回地址 (Return Address)  │
│  - 方法正常/异常退出            │
└─────────────────────────────────┘

栈内存异常

java 复制代码
# StackOverflowError:栈深度超出限制
# 示例:递归没有终止条件
public class StackOverflowDemo {
    public static void recursion() {
        recursion();  // 无限递归 → StackOverflowError
    }
    
    public static void main(String[] args) {
        recursion();
    }
}

# 配置栈大小
java -Xss2m StackOverflowDemo   # 2MB栈内存
java -Xss512k StackOverflowDemo  # 512KB栈内存
2.2.3 本地方法栈(Native Method Stack)
  • 为 JVM 执行 Native 方法(C/C++)提供服务
  • 与虚拟机栈类似,但服务对象不同
  • HotSpot 将两者合二为一
java 复制代码
/**
 * 本地方法示例
 * Object 类的大量方法都是 native 方法
 */
public class NativeMethodDemo {
    // Object 类中的 native 方法
    public native void notify();
    public native void notifyAll();
    public native void wait(long timeout);
    
    // HashMap 的 native 实现
    public native int hashCode();
}

2.3 线程共享区域详解

2.3.1 堆(Heap)
java 复制代码
/**
 * 堆是 JVM 最大的一块内存
 * 所有对象实例和数组都在堆中分配
 */
public class HeapDemo {
    public static void main(String[] args) {
        // 对象在堆中分配
        User user = new User();
        
        // 数组在堆中分配
        int[] arr = new int[1024];
        
        // 包装类和字符串常量也在堆中
        Integer i = 100;  // 自动装箱,堆中分配
        String s = "hello";  // 字符串常量池
    }
}

JDK7/8 堆内存对比

java 复制代码
JDK7 堆结构:
┌────────────────────────────────────┐
│                堆                   │
│  ┌──────┐ ┌───────┐ ┌──────────┐  │
│  │ Eden │ │S0/S1  │ │  Old Gen │  │
│  │      │ │       │ │  永久代   │  │
│  │      │ │       │ │(类信息等) │  │
│  └──────┘ └───────┘ └──────────┘  │
└────────────────────────────────────┘

JDK8 堆结构:
┌────────────────────────────────────┐
│                堆                   │
│  ┌──────┐ ┌───────┐ ┌──────────┐  │
│  │ Eden │ │S0/S1  │ │  Old Gen │  │
│  │      │ │       │ │          │  │
│  └──────┘ └───────┘ └──────────┘  │
└────────────────────────────────────┘
┌────────────────────────────────────┐
│              元空间 (MetaSpace)     │
│  (本地内存,不再占用堆空间)          │
└────────────────────────────────────┘

JDK8 元空间变化

对比项 JDK7 永久代 JDK8 元空间
位置 堆内存 本地内存(操作系统内存)
大小 固定,难以调整 动态扩展,受物理内存限制
字符串常量池 永久代 移至堆
类元数据 永久代 元空间
OOM风险 容易 Full GC 很少 OOM
2.3.2 方法区(Method Area)
java 复制代码
/**
 * 方法区存储:
 * 1. 类元信息(类名、修饰符、父类等)
 * 2. 运行时常量池
 * 3. 静态变量(JDK8 移至堆)
 * 4. 方法字节码
 */
public class MethodAreaDemo {
    
    // 静态变量 → 方法区(JDK8移至堆)
    public static int staticVar = 100;
    
    // 常量 → 运行时常量池
    public static final int CONSTANT = 200;
    
    // 方法信息 → 方法区
    public void method() {
        String s = "hello";  // 字符串常量池
    }
}

2.4 直接内存(Direct Memory)

java 复制代码
/**
 * 直接内存不受 JVM 堆大小限制
 * 用于 NIO 的 DirectByteBuffer
 * 减少 Java 堆和本地堆之间的数据复制
 */
public class DirectMemoryDemo {
    public static void main(String[] args) {
        // 分配直接内存
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024 * 100);  // 100MB
        
        // 适用场景:
        // 1. 大文件读写
        // 2. 网络通信
        // 3. 高性能缓存
    }
}

配置直接内存大小

-XX:MaxDirectMemorySize=512m

第三部分:类加载子系统(Class Loader)

3.1 类加载完整流程

java 复制代码
类加载的 5 个阶段
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  加载 (Loading)           验证 (Verification)
┌────────────────┐      ┌────────────────┐
│ 1. 通过类的    │      │ 1. 文件格式验证 │
│    全限定名    │      │    (魔数检查)   │
│    获取.class  │  →   │ 2. 元数据验证   │
│    文件字节流  │      │    (语义分析)   │
│                │      │ 3. 字节码验证   │
│ 2. 将字节流    │      │    (指令验证)   │
│    转为方法区  │      │ 4. 符号引用验证 │
│    的类元数据  │      │    (类型检查)   │
│                │      └────────────────┘
│ 3. 在堆中生成  │              ↑
│    Class对象   │              │
│    作为入口    │              │
└────────────────┘              │
         │                      │
         ↓                      │
  准备 (Preparation)            │
┌────────────────┐              │
│ 1. 为静态变量  │              │
│    分配内存    │              │
│                │      ┌──────┴────────┐
│ 2. 设置默认值  │  →   │     解析        │
│    (int→0)     │      │   (Resolution)  │
│                │      ├────────────────┤
│ 3. 不执行      │      │ 将符号引用转换   │
│    赋值语句    │      │ 为直接引用      │
│                │      │                │
└────────────────┘      │ 1. 类/接口解析  │
                        │ 2. 字段解析     │
                        │ 3. 方法解析     │
                        │ 4. 接口方法解析  │
                        └────────────────┘
                                ↑
  初始化 (Initialization)       │
┌────────────────┐              │
│ 1. 执行<clinit>│──────────────┘
│    ()方法      │
│    (静态代码块) │
│                │
│ 2. 真正执行    │
│    赋値语句    │
│    (int a=10)  │
│                │
│ 3. 线程安全    │
│    (JVM保证)   │
└────────────────┘

3.2 类加载时机(主动使用)

java 复制代码
/**
 * 类加载的触发时机(主动使用)
 */
public class ClassLoadingTriggers {
    
    // 触发1:创建类的实例
    public void trigger1() {
        User user = new User();  // 触发加载
    }
    
    // 触发2:调用类的静态方法
    public void trigger2() {
        User.staticMethod();  // 触发加载
    }
    
    // 触发3:访问类的静态变量
    public void trigger3() {
        int x = User.staticVar;  // 触发加载
    }
    
    // 触发4:反射
    public void trigger4() throws ClassNotFoundException {
        Class<?> clazz = Class.forName("com.example.User");  // 触发加载
    }
    
    // 触发5:初始化子类,父类先初始化
    public void trigger5() {
        // Parent 会先被加载
        Child child = new Child();
    }
    
    // 触发6:启动类(main方法所在类)
    public static void main(String[] args) {
        // main方法所在类会被加载
    }
}

3.3 类加载器层次结构

java 复制代码
类加载器层次
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

                    Bootstrap ClassLoader
                    (启动类加载器)
                    ┌──────────────────┐
                    │ 加载 Java 核心类 │
                    │ $JAVA_HOME/jre/  │
                    │   lib/rt.jar     │
                    │   tools.jar      │
                    │   resources.jar  │
                    └────────┬─────────┘
                             │ 父加载器为 null
                             ↓
              ┌───────────────────────────┐
              │    Extension ClassLoader   │
              │      (扩展类加载器)        │
              │ $JAVA_HOME/jre/lib/ext/*  │
              └─────────────┬─────────────┘
                            │ 父加载器为 Bootstrap
                            ↓
    ┌─────────────────────────────────────┐
    │       Application ClassLoader        │
    │         (应用类加载器)               │
    │  加载 classpath 下的所有类           │
    │  -XX:+TraceClassLoading 观察加载   │
    └─────────────┬───────────────────────┘
                  │ 父加载器为 Extension
                  ↓
        ┌──────────────────┐
        │ CustomClassLoader │
        │ (自定义类加载器)  │
        │   可打破双亲委派   │
        └──────────────────┘

3.4 双亲委派模型(Parent Delegation Model)

java 复制代码
/**
 * 双亲委派模型工作流程
 * 
 * 当类加载器收到加载请求时:
 * 1. 先委派给父类加载器处理
 * 2. 父类无法加载时,才自己加载
 */
public class ParentDelegationDemo {
    
    public static void main(String[] args) {
        // 加载 java.lang.String
        // 1. Application ClassLoader 收到请求
        // 2. 委派给 Extension ClassLoader
        // 3. Extension 委派给 Bootstrap ClassLoader
        // 4. Bootstrap 找到了 java.lang.String
        // 5. 自己加载,返回 Class 对象
        
        String s = "hello";
        // 不可能自定义一个 java.lang.String
        // 因为 Bootstrap 已经加载了核心类
    }
}

工作流程图

java 复制代码
类加载请求
    │
    ↓
┌─────────────────────────────┐
│  Application ClassLoader    │
│  收到加载 "com.example.User"│
└─────────────┬───────────────┘
              │ 委派给父加载器
              ↓
┌─────────────────────────────┐
│  Extension ClassLoader      │
│  收到加载 "com.example.User"│
└─────────────┬───────────────┘
              │ 委派给父加载器
              ↓
┌─────────────────────────────┐
│  Bootstrap ClassLoader      │
│  收到加载 "com.example.User"│
└─────────────┬───────────────┘
              │ 无法加载
              ↓
┌─────────────────────────────┐
│  Extension ClassLoader      │
│  尝试加载 "com.example.User"│
│  ✓ 在 ext 目录下找到并加载  │
└─────────────────────────────┘

双亲委派的优势

  1. 避免类重复加载:父类已加载,子类无需再加载
  2. 安全性保障 :防止核心类被篡改(如自定义 java.lang.String

3.5 破坏双亲委派模型

场景1:JDBC 驱动加载(线程上下文类加载器)
java 复制代码
/**
 * JDBC 驱动的破坏场景
 * 
 * JDBC API 在 rt.jar 中,由 Bootstrap 加载
 * 但 JDBC 驱动(如 MySQL、Oracle)由第三方实现
 * 驱动在 classpath 外,无法被父加载器加载
 */
public class JDBCDelegationBreak {
    public static void main(String[] args) {
        // 这种方式需要先手动加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        
        // 但 SPI 机制通过线程上下文类加载器解决
        // ServiceLoader 使用 Thread.currentThread().getContextClassLoader()
    }
}
场景2:TOMCAT 类隔离
java 复制代码
/**
 * TOMCAT 的类隔离机制
 * 
 * 一个服务器运行多个 Web 应用
 * 每个应用可能依赖不同版本的同一个类
 * 必须打破双亲委派,实现应用隔离
 */
public class TomcatIsolation {
    // Tomcat 的类加载器结构:
    // 1. Bootstrap ClassLoader
    // 2. System ClassLoader
    // 3. Common ClassLoader (共享库)
    // 4. WebApp ClassLoader (每个Web应用独立)
    //       ↑
    //   打破了双亲委派!
}
场景3:OSGi 模块化
java 复制代码
/**
 * OSGi 的类加载机制
 * 
 * 每个 Bundle(模块)有自己的类加载器
 * 可以精确控制类的可见性
 * 完全打破双亲委派
 */
public class OSGiBreak {
    // OSGi 加载顺序:
    // 1. Bundle 自身类加载器
    // 2. Import Package 类加载器
    // 3. Require Bundle 类加载器
    // 4. Bootstrap ClassLoader
}

3.6 自定义类加载器

java 复制代码
/**
 * 自定义类加载器示例
 * 应用场景:热部署、加密类加载、类隔离
 */
public class CustomClassLoader extends ClassLoader {
    
    private String basePath;
    
    public CustomClassLoader(String basePath) {
        this.basePath = basePath;
    }
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 1. 将类名转换为文件路径
        String fileName = name.replace('.', '/') + ".class";
        String fullPath = basePath + fileName;
        
        // 2. 读取字节流
        try (FileInputStream fis = new FileInputStream(fullPath);
             ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            
            // 3. 调用 defineClass 将字节流转为 Class 对象
            byte[] classData = bos.toByteArray();
            return defineClass(name, classData, 0, classData.length);
            
        } catch (IOException e) {
            throw new ClassNotFoundException("Class not found: " + name, e);
        }
    }
    
    // 重写 loadClass 打破双亲委派
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        // 优先自己加载(打破双亲委派)
        if (name.startsWith("com.myapp.")) {
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                c = findClass(name);
            }
            return c;
        }
        // 其他类走双亲委派
        return super.loadClass(name);
    }
    
    public static void main(String[] args) throws Exception {
        CustomClassLoader loader = new CustomClassLoader("/path/to/classes/");
        Class<?> clazz = loader.loadClass("com.myapp.User");
        Object obj = clazz.getDeclaredConstructor().newInstance();
        System.out.println("自定义类加载成功: " + obj.getClass().getClassLoader());
    }
}

第四部分:垃圾回收机制(GC)

4.1 对象存活判断算法

4.1.1 引用计数法(已废弃)
java 复制代码
/**
 * 引用计数法
 * 
 * 对象每被引用一次,计数器+1
 * 引用断开时,计数器-1
 * 计数器为0时,可回收
 * 
 * 问题:无法解决循环引用
 */
public class ReferenceCounting {
    public static void main(String[] args) {
        // objA 和 objB 相互引用,但都没有被外部引用
        // 引用计数都为1,但实际上已经不可达
        objA.ref = objB;
        objB.ref = objA;
        objA = null;
        objB = null;
        // GC 无法回收这两个对象(循环引用问题)
    }
}
4.1.2 可达性分析算法(GC Roots)
java 复制代码
/**
 * 可达性分析算法
 * 
 * 从 GC Roots 出发,向下搜索
 * 能到达的对象是存活的
 * 不能到达的对象可回收
 */
public class GC RootsDemo {
    
    // GC Roots 包括:
    
    // 1. 虚拟机栈中引用的对象
    public GCRootsDemo obj1 = new GCRootsDemo();
    
    public void method() {
        // 局部变量表中的对象引用
        Object local = new Object();
    }
    
    // 2. 方法区中静态变量引用的对象
    public static Object staticObj = new Object();
    
    // 3. 方法区中常量引用的对象
    public static final Object CONSTANT_OBJ = new Object();
    
    // 4. 本地方法栈中 JNI 引用的对象
    // native 方法持有的对象引用
    
    public static void main(String[] args) {
        // local 变量是 GC Root
        // staticObj 是 GC Root
        // 常量引用是 GC Root
    }
}

GC Roots 完整列表

java 复制代码
GC Roots 包含:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. 虚拟机栈中引用的对象
   - 方法中局部变量表引用的对象
   - 栈帧中操作数栈引用的对象

2. 本地方法栈中 JNI 引用的对象
   - native 方法中引用的对象

3. 方法区中类静态属性引用的对象
   - static 修饰的引用类型变量

4. 方法区中常量引用的对象
   - static final 修饰的引用类型常量

5. JVM 内置类加载器引用的对象
   - Class 对象等

6. 正在持有的锁对象
   - synchronized 锁住的对象

7. JVM 内部引用
   - 异常对象、类加载器等
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

4.2 引用类型详解

java 复制代码
/**
 * 四种引用类型及回收时机
 */
public class ReferenceTypes {
    
    // 1. 强引用 - 最常见的引用
    // 无论内存是否充足,都不会被回收
    Object strongRef = new Object();  // GC 不会回收
    
    // 2. 软引用 - 内存不足时回收
    // SoftReference 用于缓存
    SoftReference<Object> softRef = new SoftReference<>(new Object());
    
    // 3. 弱引用 - 下次 GC 时回收
    // WeakReference 用于防止内存泄漏
    WeakReference<Object> weakRef = new WeakReference<>(new Object());
    
    // 4. 虚引用 - 随时可能被回收
    // 必须配合 ReferenceQueue,用于跟踪对象回收
    ReferenceQueue<Object> queue = new ReferenceQueue<>();
    PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);
}

引用类型对比

引用类型 回收时机 典型应用
强引用 永不回收 普通对象引用
软引用 内存不足时 图片缓存
弱引用 下次 GC ThreadLocal Map
虚引用 随时可回收 NIO 直接内存管理

4.3 垃圾回收算法

4.3.1 标记-清除算法(Mark-Sweep)
java 复制代码
┌─────────────────────────────────────────┐
│  标记-清除算法                          │
│                                         │
│  步骤1:标记所有需要回收的对象           │
│  ┌───────────────────────────────┐     │
│  │  存活对象:√  死亡对象:×      │     │
│  │                               │     │
│  │  □ □ □ □ □ □ □ □ □ □       │     │
│  │  √ × √ √ × √ × √ √ ×       │     │
│  │                               │     │
│  └───────────────────────────────┘     │
│                                         │
│  步骤2:清除死亡对象                     │
│  ┌───────────────────────────────┐     │
│  │  清除后(产生内存碎片)       │     │
│  │                               │     │
│  │  □ □ □ □       □ □ □ □ □   │     │
│  │  (空闲)    (空闲)         │     │
│  │  无法分配大对象               │     │
│  └───────────────────────────────┘     │
└─────────────────────────────────────────┘

优点:实现简单
缺点:内存碎片、效率不稳定
4.3.2 复制算法(Copying)
java 复制代码
┌─────────────────────────────────────────┐
│  复制算法                               │
│                                         │
│  将内存分为两块,每次只用一块           │
│                                         │
│  步骤1:使用区 A                        │
│  ┌───────────────────────────────┐     │
│  │  区域 A(使用中)              │     │
│  │  存活对象                      │     │
│  │                               │     │
│  │  √ □ √ □ □ √ □ □ □ □       │     │
│  └───────────────────────────────┘     │
│                                         │
│  步骤2:存活对象复制到区域 B            │
│  ┌─────────────────┐ ┌───────────────┐ │
│  │  区域 A(清空)  │ │  区域 B       │ │
│  │                 │ │  复制存活对象 │ │
│  │                 │ │  √ √ √       │ │
│  └─────────────────┘ └───────────────┘ │
│                                         │
│  步骤3:交换使用区域                    │
│  ┌─────────────────┐ ┌───────────────┐ │
│  │  区域 A         │ │  区域 B       │ │
│  │                 │ │  复制存活对象 │ │
│  │                 │ │  √ √ √       │ │
│  └─────────────────┘ └───────────────┘ │
└─────────────────────────────────────────┘

优点:无内存碎片、效率高
缺点:内存利用率低(50%)
适用:新生代(对象存活率低)
4.3.3 标记-整理算法(Mark-Compact)
java 复制代码
┌─────────────────────────────────────────┐
│  标记-整理算法                          │
│                                         │
│  步骤1:标记存活对象                     │
│  ┌───────────────────────────────┐     │
│  │  √ × √ √ × √ × √ × √       │     │
│  └───────────────────────────────┘     │
│                                         │
│  步骤2:整理,存活对象移向一端           │
│  ┌───────────────────────────────┐     │
│  │  存活对象     │    空闲区域    │     │
│  │  √ √ √ √ √ √ │  × × × ×      │     │
│  │               ↑               │     │
│  │            移动到这里          │     │
│  └───────────────────────────────┘     │
│                                         │
│  优点:无碎片、内存利用率高              │
│  缺点:移动对象开销大、STW时间长        │
│  适用:老年代                           │
└─────────────────────────────────────────┘

4.4 分代收集理论

java 复制代码
分代收集策略
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

┌────────────────────────────────────────────────────────┐
│                          堆内存                         │
│                                                        │
│  ┌─────────────────────┐  ┌─────────────────────────┐ │
│  │       新生代         │  │         老年代           │ │
│  │   Young Generation   │  │    Old Generation       │ │
│  │                      │  │                         │ │
│  │  ┌────┐┌────┐┌────┐ │  │  ┌───────────────────┐  │ │
│  │  │Eden││ S0 ││ S1 │ │  │  │                   │  │ │
│  │  │ 8份 ││ 1份 ││ 1份│ │  │  │                   │  │ │
│  │  └────┘└────┘└────┘ │  │  │   存活率高的对象    │  │ │
│  │                      │  │  │                   │  │ │
│  │  ┌──────────────┐   │  │  │                   │  │ │
│  │  │ Minor GC区域 │   │  │  │                   │  │ │
│  │  └──────────────┘   │  │  └───────────────────┘  │ │
│  │                      │  │                         │ │
│  │  复制算法            │  │  标记-整理/标记-清除    │ │
│  │  对象存活率低        │  │  对象存活率高           │ │
│  │  频繁收集            │  │  偶尔收集               │ │
│  └─────────────────────┘  └─────────────────────────┘ │
│                                                        │
│  ┌────────────────────────────────────────────────┐   │
│  │                   元空间(本地内存)            │   │
│  │                   类信息、常量等                 │   │
│  └────────────────────────────────────────────────┘   │
└────────────────────────────────────────────────────────┘

对象流转过程
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

new User()
    │
    ↓ 分配到 Eden 区
┌─────────┐
│  Eden   │ ←── 通常新对象在这里分配
└────┬────┘
     │ Minor GC 后,存活对象移入 S0
     ↓
┌────┴────┐
│ S0 或 S1 │ ←── 每次 Minor GC 后,交换使用
└────┬────┘
     │ 多次 Minor GC 后,对象进入老年代
     ↓
┌─────────┐
│  Old    │ ←── 长期存活的对象
└─────────┘
     │
     │ 触发条件
     ↓
┌─────────┐
│  Full   │ ←── 整个堆+元空间
│   GC    │
└─────────┘

4.5 垃圾收集器详解

4.5.1 收集器全景图
java 复制代码
JVM 垃圾收集器家族
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

                          新生代                                    老年代
                         ┌──────┐                               ┌──────────┐
                         │      │                               │          │
            ┌────────────┤Serial├────────────┐                 │          │
            │            │      │            │                 │          │
            │            └──┬───┘            │                 │          │
            ↓               ↓                ↓                 │          │
         ┌──────┐        ┌──────────┐     ┌────────────┐       │          │
         │ParNew│        │ Parallel │     │  Parallel  │       │          │
         │      │        │ Scavenge │     │   Old      │       │          │
         └──┬───┘        └──────────┘     └─────┬──────┘       │          │
            │                                    │              │          │
            │                                    │              │          │
            ↓                                    ↓              │          │
         ┌──────┐                            ┌────────┐        │          │
         │ CMS  │←───────────────────────────│  G1    │←───────┤          │
         │      │                            │        │        │          │
         └──┬───┘                            └───┬────┘        │          │
            │                                   │              │          │
            │                                   │              │          │
            ↓                                   ↓              ↓          │
         ┌────────┐                        ┌────────┐    ┌──────────┐  │
         │ Serial │                        │ ZGC    │    │Shenandoah│  │
         │  Old   │                        │        │    │          │  │
         └────────┘                        └────────┘    └──────────┘  │
                                                                  │
                                                                  ↓
                                                            ┌──────────┐
                                                            │  Epsilon │
                                                            │ (无操作) │
                                                            └──────────┘

年轻代收集器组合(红色箭头)
老年代收集器组合(蓝色箭头)
4.5.2 Serial 收集器
java 复制代码
/**
 * Serial 收集器
 * 
 * 特点:
 * - 单线程收集
 * - GC 时暂停所有用户线程(Stop The World)
 * - 简单高效,适合客户端应用
 */
public class SerialCollector {
    // 启用参数:-XX:+UseSerialGC
    // 新生代:Serial(复制算法)
    // 老年代:Serial Old(标记-整理)
}
4.5.3 ParNew 收集器
java 复制代码
/**
 * ParNew 收集器
 * 
 * 特点:
 * - Serial 的多线程版本
 * - 默认线程数 = CPU 核心数
 * - 响应优先,配合 CMS 使用
 */
public class ParNewCollector {
    // 启用参数:-XX:+UseParNewGC
    // 新生代:ParNew(复制算法)
    // 老年代:CMS 或 Serial Old
}
4.5.4 Parallel Scavenge 收集器
java 复制代码
/**
 * Parallel Scavenge 收集器
 * 
 * 特点:
 * - 吞吐量优先
 * - 可控制最大停顿时间和吞吐量
 * - 自适应调节策略
 */
public class ParallelScavengeCollector {
    // 启用参数:-XX:+UseParallelGC
    // 新生代:Parallel Scavenge(复制算法)
    // 老年代:Parallel Old(标记-整理)
    
    // 关键参数:
    // -XX:MaxGCPauseMillis=100  // 最大停顿时间
    // -XX:GCTimeRatio=99        // 吞吐量 = 99%
    // -XX:+UseAdaptiveSizePolicy // 自适应调节
}
4.5.5 CMS 收集器(Concurrent Mark Sweep)
java 复制代码
/**
 * CMS 收集器
 * 
 * 目标:低停顿、低延迟
 * 算法:标记-清除
 * 
 * 工作流程:
 * 1. 初始标记(STW)- 标记 GC Roots 直接关联的对象
 * 2. 并发标记 - 遍历 GC Roots 引用链
 * 3. 重新标记(STW)- 修正并发标记期间的变化
 * 4. 并发清除 - 清理死亡对象
 */
public class CMSCollector {
    // 启用参数:-XX:+UseConcMarkSweepGC
    // 新生代:ParNew
    // 老年代:CMS
    
    // 问题:
    // 1. 内存碎片(标记-清除)
    // 2. 并发阶段占用 CPU
    // 3. 无法处理浮动垃圾(并发时新产生的垃圾)
    
    // 参数调优:
    // -XX:CMSInitiatingOccupancyFraction=70  // 老年代使用率70%时触发
    // -XX:+UseCMSCompactAtFullCollection    // FullGC时整理
    // -XX:CMSFullGCsBeforeCompaction=5       // 5次FullGC后整理
}

CMS 工作流程图

java 复制代码
CMS 工作流程
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

    初始标记 (STW)
    ├─ 停顿时间:短
    └─ 标记:GC Roots 直接关联的对象
          │
          ↓
    ┌─────────────┐
    │  并发标记   │ ←── 用户线程并发执行
    ├─────────────┤
    │ 遍历GC Roots│
    │ 引用链      │
    │ 标记存活对象│
    └──────┬──────┘
           │ 用户线程继续运行
           ↓ 产生新的垃圾(浮动垃圾)
    重新标记 (STW)
    ├─ 停顿时间:较长
    └─ 修正并发标记期间产生的变化
           │
           ↓
    ┌─────────────┐
    │  并发清除   │ ←── 用户线程并发执行
    ├─────────────┤
    │ 清除死亡   │
    │ 对象       │
    └─────────────┘
4.5.6 G1 收集器(Garbage First)
java 复制代码
/**
 * G1 收集器
 * 
 * 思想:将堆划分为多个大小相等的 Region
 * 优先回收价值最高的 Region(垃圾最多)
 * 兼顾吞吐量和低延迟
 * 
 * 特点:
 * - 面向服务端应用
 * - 可以设置期望停顿时间(-XX:MaxGCPauseMillis)
 * - 整合多种算法(标记-整理、复制)
 * - 没有内存碎片
 */
public class G1Collector {
    // 启用参数:-XX:+UseG1GC
    
    // Region 结构:
    // - Eden Region(E)
    // - Survivor Region(S)
    // - Old Region(O)
    // - Humongous Region(H)// 大对象,超过Region一半的对象
    
    // G1 的工作流程:
    // 1. Young GC:收集年轻代的 Eden/Survivor
    // 2. Mixed GC:年轻代 + 价值最高的老年代 Region
    
    // 关键参数:
    // -XX:MaxGCPauseMillis=200    // 最大停顿时间
    // -XX:G1NewSizePercent=5      // 年轻代最小比例
    // -XX:G1MaxNewSizePercent=60  // 年轻代最大比例
}

G1 Region 结构

java 复制代码
G1 堆内存布局
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

┌─────────────────────────────────────────────────────────┐
│                        堆内存                           │
│                                                         │
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐  │
│  │ E  │ │ E  │ │ S  │ │ O  │ │ O  │ │ H  │ │ O  │  │
│  └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘  │
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐  │
│  │ E  │ │ E  │ │ O  │ │ O  │ │ O  │ │ E  │ │ E  │  │
│  └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘  │
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐  │
│  │ O  │ │ O  │ │ E  │ │ E  │ │ S  │ │ O  │ │ O  │  │
│  └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘  │
│                                                         │
│  E = Eden Region    S = Survivor Region                │
│  O = Old Region     H = Humongous Region               │
│                                                         │
│  每个 Region 大小:1MB - 32MB(2的幂次)               │
│  整个堆最多 2048 个 Region                              │
└─────────────────────────────────────────────────────────┘
4.5.7 ZGC 收集器
java 复制代码
/**
 * ZGC(Z Garbage Collector)
 * 
 * 目标:亚毫秒级停顿,最大停顿时间不超过 10ms
 * 算法:并发标记 + 增量更新 + 读屏障
 * 特点:
 * - 不分代(但有分代思想)
 * - 着色指针(Colored Pointers)
 * - 读屏障(Load Barrier)
 * - 支持 TB 级堆内存
 */
public class ZGCCollector {
    // 启用参数:JDK 11+  -XX:+UseZGC
    // 参数:
    // -XX:MaxGCPauseMillis=10    // 最大停顿10ms
    // -XX:ConcGCThreads=4        // 并发GC线程数
}
4.5.8 Shenandoah 收集器
java 复制代码
/**
 * Shenandoah
 * 
 * 目标:低延迟
 * 类似 ZGC,但使用转发指针(Brooks Pointer)
 * 支持 OpenJDK
 */
public class ShenandoahCollector {
    // 启用参数:-XX:+UseShenandoahGC
}
4.5.9 收集器对比总结
收集器 工作区域 算法 并发 停顿时间 吞吐量 适用场景
Serial 新生代 复制 单线程 长(STW) 客户端、单核
ParNew 新生代 复制 多线程 配合CMS
Parallel Scavenge 新生代 复制 多线程 可调节 后台批处理
Serial Old 老年代 标记-整理 单线程 客户端
Parallel Old 老年代 标记-整理 多线程 可调节 后台批处理
CMS 老年代 标记-清除 并发 互联网应用
G1 整堆 标记-整理+复制 并发 可预测 中高 替代CMS
ZGC 整堆 标记-整理 并发 <10ms 大内存低延迟
Shenandoah 整堆 标记-整理 并发 低延迟

第五部分:字节码与执行引擎

5.1 字节码文件结构

java 复制代码
/**
 * 字节码文件结构
 */
public class BytecodeStructure {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int c = a + b;
        System.out.println(c);
    }
}

字节码结构图

java 复制代码
Class 文件结构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

┌─────────────────────────────────────────────────────────┐
│  魔数 (Magic Number)                                    │
│  0xCAFEBABE - 标识这是 Java class 文件                 │
├─────────────────────────────────────────────────────────┤
│  主次版本号 (Major/Minor Version)                       │
│  52.0 = JDK 8                                         │
├─────────────────────────────────────────────────────────┤
│  常量池 (Constant Pool)                                 │
│  ┌────────────┐ ┌────────────┐ ┌────────────┐       │
│  │ #1 Method  │ │ #2 Class   │ │ #3 UTF8    │       │
│  │ref: #2 #4  │ │name: #3    │ │"main"      │       │
│  └────────────┘ └────────────┘ └────────────┘       │
├─────────────────────────────────────────────────────────┤
│  访问标志 (Access Flags)                               │
│  ACC_PUBLIC | ACC_SUPER (0x0001 | 0x0020)             │
├─────────────────────────────────────────────────────────┤
│  类索引 (This Class) - 指向常量池                      │
│  #1 = com/example/BytecodeStructure                   │
├─────────────────────────────────────────────────────────┤
│  父类索引 (Super Class) - 指向常量池                   │
│  #3 = java/lang/Object                               │
├─────────────────────────────────────────────────────────┤
│  接口表 (Interfaces)                                    │
│  空(没有实现接口)                                    │
├─────────────────────────────────────────────────────────┤
│  字段表 (Fields)                                       │
│  空(没有字段)                                        │
├─────────────────────────────────────────────────────────┤
│  方法表 (Methods)                                       │
│  ┌────────────────────────────────────────────────┐   │
│  │  main 方法                                      │   │
│  │  ┌──────┐ ┌────────────┐ ┌─────────────────┐   │   │
│  │  │ 权限 │ │ 方法名 #4  │ │ 描述符 (#5)     │   │   │
│  │  │ACC   │ │ "main"    │ │ "([Ljava/lang/  │   │   │
│  │  │PUBLIC│ │           │ │  String;)V"     │   │   │
│  │  │STATIC│ │           │ │                 │   │   │
│  │  └──────┘ └────────────┘ └─────────────────┘   │   │
│  │  Code 属性:字节码指令                           │   │
│  │  ┌────────────────────────────────────────┐    │   │
│  │  │ bipush 10      // 将 10 压入栈          │    │   │
│  │  │ istore_1       // 存储到局部变量表 slot1 │    │   │
│  │  │ bipush 20      // 将 20 压入栈          │    │   │
│  │  │ istore_2       // 存储到局部变量表 slot2 │    │   │
│  │  │ iload_1        // 加载 slot1 的值       │    │   │
│  │  │ iload_2        // 加载 slot2 的值       │    │   │
│  │  │ iadd           // 相加                   │    │   │
│  │  │ istore_3       // 存储到局部变量表 slot3 │    │   │
│  │  │ getstatic #6   // 获取 System.out       │    │   │
│  │  │ iload_3        // 加载 slot3 的值       │    │   │
│  │  │ invokevirtual #7 // 调用 println        │    │   │
│  │  │ return         // 返回                  │    │   │
│  │  └────────────────────────────────────────┘    │   │
│  └────────────────────────────────────────────────┘   │
├─────────────────────────────────────────────────────────┤
│  属性表 (Attributes)                                    │
│  SourceFile、LineNumberTable 等                        │
└─────────────────────────────────────────────────────────┘

5.2 常见字节码指令

java 复制代码
/**
 * 字节码指令分类
 */
public class BytecodeInstructions {
    
    // ============ 加载存储指令 ============
    public void loadStore() {
        int a = 10;          // bipush 10 / istore_1
        int b = a + 20;      // iload_1 / bipush 20 / iadd / istore_2
    }
    // iload_n: 从局部变量表加载 int 到栈
    // istore_n: 从栈存储 int 到局部变量表
    // bipush: 加载 byte 常量
    // sipush: 加载 short 常量
    // iconst_0~5: 加载 int 常量 0~5
    
    // ============ 运算指令 ============
    public int arithmetic() {
        int a = 10, b = 20;
        return a + b;        // iadd
    }
    public int multiply() {
        int a = 5, b = 6;
        return a * b;        // imul
    }
    // iadd, isub, imul, idiv, irem
    // iand, ior, ixor  // 按位与、或、异或
    
    // ============ 类型转换 ============
    public void typeCast() {
        int a = 10;
        long b = a;          // i2l
        double c = a;        // i2d
    }
    // i2l, i2f, i2d, l2i, l2f, l2d 等
    
    // ============ 对象创建与访问 ============
    public Object createObject() {
        Object obj = new Object();  // new / dup / invokespecial
        return obj;
    }
    // new: 创建对象
    // dup: 复制栈顶值
    // getfield: 获取字段
    // putfield: 设置字段
    
    // ============ 方法调用 ============
    public void methodInvoke() {
        methodA();                    // invokevirtual
    }
    public static void methodA() {}   // invokestatic
    // invokevirtual: 调用虚方法(多态)
    // invokestatic: 调用静态方法
    // invokespecial: 调用构造器/私有方法/父类方法
    // invokedynamic: 调用动态方法(Lambda)
    
    // ============ 控制转移 ============
    public void controlTransfer() {
        int a = 10;
        if (a > 5) {         // ifle 标签 / goto 标签
            System.out.println("a > 5");
        }
        for (int i = 0; i < 10; i++) {}  // 循环
    }
    // ifeq, ifne, iflt, ifgt, ifle, if_icmpXX
    // goto, goto_w
    // tableswitch, lookupswitch
    
    // ============ 异常处理 ============
    public void exception() {
        try {
            throw new RuntimeException();
        } catch (RuntimeException e) {
            // athrow
        }
    }
    // athrow: 抛出异常
    
    // ============ 同步指令 ============
    public synchronized void sync() {
        synchronized (this) {
            // monitorenter / monitorexit
        }
    }
    // monitorenter: 进入同步块
    // monitorexit: 退出同步块
}

5.3 执行引擎工作原理

java 复制代码
执行引擎架构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

┌─────────────────────────────────────────────────────────┐
│                    字节码 (.class)                      │
└─────────────────────────┬───────────────────────────────┘
                          ↓
              ┌───────────────────────┐
              │      解释器           │
              │   (Interpreter)       │
              │                       │
              │  逐行解释执行         │
              │  启动快,执行慢        │
              └───────────┬───────────┘
                          │
                          ↓
    ┌───────────────────────────────────────────┐
    │               热点代码探测                  │
    │  ┌─────────────────────────────────────┐ │
    │  │  方法调用计数器                      │ │
    │  │  循环回边计数器                      │ │
    │  │                                     │ │
    │  │  阈值:10000次 或 热点探测           │ │
    │  └─────────────────────────────────────┘ │
    └───────────────────────┬───────────────────┘
                            │
                            ↓ 热点代码达到阈值
              ┌───────────────────────┐
              │     JIT 编译器        │
              │  (Just-In-Time)      │
              │                       │
              │  编译为本地机器码      │
              │  执行快,启动慢        │
              │                       │
              │  ┌──────────────────┐ │
              │  │ C1 编译器        │ │
              │  │ 客户端优化       │ │
              │  └──────────────────┘ │
              │  ┌──────────────────┐ │
              │  │ C2 编译器        │ │
              │  │ 服务端优化       │ │
              │  └──────────────────┘ │
              └───────────┬───────────┘
                          │
                          ↓
              ┌───────────────────────┐
              │     本地机器码        │
              │    (Native Code)      │
              └───────────────────────┘

5.4 JIT 编译器优化技术

java 复制代码
/**
 * JIT 编译器的优化技术
 */
public class JITOptimization {
    
    // 1. 方法内联 (Method Inlining)
    // 将被调用方法直接展开到调用处,减少调用开销
    public void inlineDemo() {
        // 编译器会内联 add 方法
        int result = add(1, 2);
    }
    
    private int add(int a, int b) {
        return a + b;
    }
    // 内联后:
    // int result = 1 + 2;
    
    // 2. 逃逸分析 (Escape Analysis)
    // 分析对象的动态作用域
    public void escapeDemo() {
        // 锁消除:methodSync 未逃逸,锁可消除
        methodSync();
    }
    
    private synchronized void methodSync() {
        // 锁对象未逃逸,安全消除锁
    }
    
    // 3. 栈上分配 (Stack Allocation)
    // 未逃逸的对象直接在栈上分配,不进入堆
    public void stackAllocDemo() {
        // point 未逃逸,栈上分配
        Point p = new Point(1, 2);
        System.out.println(p.x);
    }
    
    // 4. 标量替换 (Scalar Replacement)
    // 将对象拆解为原始类型,减少对象分配
    public void scalarDemo() {
        // 编译器可能直接用 x=1, y=2 替代 Point 对象
        Point p = new Point(1, 2);
        use(p.x, p.y);
    }
    
    // 5. 公共子表达式消除
    public void cseDemo() {
        int a = (b + c) + (b + c);  // 优化为 tmp = b + c; a = tmp + tmp
    }
    
    // 6. 数组边界检查消除
    public void boundsCheckElimination() {
        int[] arr = new int[10];
        for (int i = 0; i < 10; i++) {
            arr[i] = i;  // JIT 判断 i 永远不会越界,消除边界检查
        }
    }
    
    static class Point {
        int x, y;
        Point(int x, int y) { this.x = x; this.y = y; }
    }
    
    static void use(int x, int y) {}
}

第六部分:实战调优

6.1 JVM 参数大全

java 复制代码
# ==================== 内存参数 ====================
# 堆内存
-Xms512m              # 堆初始大小
-Xmx512m              # 堆最大大小(生产环境建议与Xms相同)
-Xmn128m              # 新生代大小
-XX:NewRatio=2        # 新生代:老年代 = 1:2
-XX:SurvivorRatio=8   # Eden:Survivor = 8:1

# 元空间(JDK8+)
-XX:MetaspaceSize=256m   # 元空间初始大小
-XX:MaxMetaspaceSize=512m # 元空间最大大小

# 线程栈
-Xss1m                # 线程栈大小(默认1MB)

# 直接内存
-XX:MaxDirectMemorySize=512m  # 直接内存最大大小

# ==================== 垃圾收集器参数 ====================
# Serial
-XX:+UseSerialGC

# ParNew + CMS
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC

# Parallel
-XX:+UseParallelGC           # 新生代 Parallel Scavenge
-XX:+UseParallelOldGC        # 老年代 Parallel Old
-XX:ParallelGCThreads=4      # GC 线程数

# G1
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200     # 最大停顿时间
-XX:G1NewSizePercent=5        # 年轻代最小比例
-XX:G1MaxNewSizePercent=60    # 年轻代最大比例
-XX:InitiatingHeapOccupancyPercent=45  # 触发Mixed GC的堆使用率

# ZGC(JDK 11+)
-XX:+UseZGC
-XX:MaxGCPauseMillis=10

# ==================== GC 日志参数 ====================
# JDK 9+ 推荐格式
-Xlog:gc*:file=gc.log:time,uptime,level,tags

# JDK 8 格式
-Xloggc:gc.log
-verbose:gc
-XX:+PrintGCDetails           # 打印详细GC日志
-XX:+PrintGCDateStamps        # 打印时间戳
-XX:+PrintGCTimeStamps        # 打印GC发生时间
-XX:+PrintTenuringDistribution # 打印年龄分布
-XX:+PrintReferenceGC         # 打印 Reference 处理时间

# ==================== OOM 参数 ====================
-XX:+HeapDumpOnOutOfMemoryError           # OOM时导出堆
-XX:HeapDumpPath=/path/to/dump.hprof      # 堆转储路径
-XX:+ExitOnOutOfMemoryError               # OOM时退出(JDK 8u92+)
-XX:+CrashOnOutOfMemoryError              # OOM时crash(JDK 8u94+)

# ==================== 性能参数 ====================
-XX:+AlwaysPreTouch               # 启动时预分配并访问所有堆内存
-XX:PrefetchLines=2               # 预取行数
-XX:+UseStringDeduplication       # 字符串去重(JDK 8u20+)
-XX:+OptimizeStringConcat         # 优化字符串拼接

# ==================== JIT 参数 ====================
-XX:CompileThreshold=10000         # 方法调用阈值触发JIT
-XX:TieredCompilation            # 分层编译(JDK 7u40+)
-XX:+TieredCompilation            # 启用分层编译

# ==================== 调试参数 ====================
-XX:+PrintCommandLineFlags        # 打印显式设置的参数
-XX:+PrintVMOptions               # 打印传递给JVM的参数
-verbose:class                   # 打印类加载信息
-XX:+TraceClassLoading           # 跟踪类加载
-XX:+UnlockCommercialVMFeatures   # 解锁商业特性

6.2 GC 日志解读

java 复制代码
# 示例 GC 日志解读

# 1. Young GC 日志
2024-01-15T10:23:45.123+0800: [GC (Allocation Failure) 
  [ParNew: 524288K->43520K(524288K), 0.0234567 secs] 
  1062400K->592320K(2097152K), 0.0245678 secs] 
  [Times: user=0.10 sys=0.01, real=0.02 secs]

解析:
- ParNew: 新生代收集(ParNew 收集器)
- 524288K->43520K: 收集前->收集后(新生代)
- 524288K: 新生代总大小
- 0.0234567 secs: GC 耗时
- 1062400K->592320K: 堆内存使用
- 2097152K: 堆总大小
- Times: user=用户CPU时间, sys=系统CPU时间, real=实际耗时

# 2. Full GC 日志
2024-01-15T10:25:30.456+0800: [Full GC (Allocation Failure) 
  [CMS: 824320K->0K(1048576K), 2.3456789 secs]
  1879048K->524288K(2097152K), 2.3467890 secs]
  [Times: user=8.12 sys=0.56, real=2.35 secs]

解析:
- Full GC: 整个堆收集
- CMS: 老年代使用 CMS 收集器
- 824320K->0K: 老年代回收前后
- 2.3456789 secs: CMS 收集耗时

# 3. CMS 日志
2024-01-15T10:25:30.456+0800: [GC (CMS Initial Mark)
  [CMS-initial-mark: 824320K(1048576K)] 824320K]
  0.0123456 secs]

2025-01-15T10:25:30.468+0800: [CMS-concurrent-mark-start]
2025-01-15T10:25:31.234+0800: [CMS-concurrent-mark-end, 0.7654321 secs]

2025-01-15T10:25:31.234+0800: [CMS-concurrent-preclean-start]
2024-01-15T10:25:31.456+0800: [CMS-concurrent-preclean-end, 0.2222222 secs]

2025-01-15T10:25:31.456+0800: [CMS-remark 
  [Rescan (parallel) , 0.1234567 secs]
  [weak refs processing, 0.0123456 secs]
  [scrub string table, 0.0034567 secs]
  0.1456789 secs]

2025-01-15T10:25:31.601+0800: [CMS-concurrent-sweep-start]
2025-01-15T10:25:32.456+0800: [CMS-concurrent-sweep-end, 0.8543210 secs]

2025-01-15T10:25:32.456+0800: [CMS-concurrent-reset-start]
2025-01-15T10:25:32.567+0800: [CMS-concurrent-reset-end, 0.1111111 secs]

6.3 调优案例

案例1:CMS 调优
java 复制代码
# 典型 CMS 配置
java -server \
  -Xms4g -Xmx4g \
  -XX:NewRatio=2 \
  -XX:SurvivorRatio=8 \
  -XX:+UseConcMarkSweepGC \
  -XX:CMSInitiatingOccupancyFraction=70 \
  -XX:+UseCMSCompactAtFullCollection \
  -XX:CMSFullGCsBeforeCompaction=5 \
  -XX:+PrintGCDetails \
  -XX:+PrintGCDateStamps \
  -Xloggc:gc.log \
  -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/var/log/heap.hprof \
  -jar app.jar

# 解释:
# -XX:CMSInitiatingOccupancyFraction=70
#   老年代使用率70%时触发CMS(避免Promotion Failed)
# -XX:+UseCMSCompactAtFullCollection
#   FullGC后整理内存,减少碎片
# -XX:CMSFullGCsBeforeCompaction=5
#   5次FullGC后进行一次整理
案例2:G1 调优
java 复制代码
# 典型 G1 配置
java -server \
  -Xms4g -Xmx4g \
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=200 \
  -XX:G1NewSizePercent=5 \
  -XX:G1MaxNewSizePercent=60 \
  -XX:InitiatingHeapOccupancyPercent=45 \
  -XX:G1HeapRegionSize=4m \
  -XX:G1ReservePercent=10 \
  -XX:ParallelGCThreads=4 \
  -XX:ConcGCThreads=2 \
  -Xlog:gc*:file=gc.log:time,uptime,level,tags \
  -XX:+HeapDumpOnOutOfMemoryError \
  -jar app.jar

# 解释:
# -XX:MaxGCPauseMillis=200
#   目标停顿时间200ms
# -XX:InitiatingHeapOccupancyPercent=45
#   堆使用率45%时开始Mixed GC
# -XX:G1HeapRegionSize=4m
#   Region大小4MB(1/2/4/8/16/32MB可选)
# -XX:G1ReservePercent=10
#   预留10%空间防止to-space overflow
案例3:ZGC 调优(JDK 11+)
java 复制代码
# ZGC 配置
java -server \
  -Xms16g -Xmx16g \
  -XX:+UseZGC \
  -XX:MaxGCPauseMillis=10 \
  -XX:+ZCollectionInterval=0 \
  -XX:ZProactive=false \
  -XX:+UnlockExperimentalVMOptions \
  -XX:+UseTransparentHugePages \
  -Xlog:gc*:file=gc.log \
  -jar app.jar

第七部分:工具链

7.1 JDK 内置工具

java 复制代码
# 1. jps - 查看 Java 进程
jps                          # 列出所有 Java 进程
jps -l                       # 显示完整类名/jar名
jps -v                       # 显示 JVM 参数

# 2. jstat - 统计信息监控
jstat -gcutil <pid> 1000    # 每秒打印 GC 统计(百分比)
jstat -gc <pid> 1000        # 每秒打印 GC 容量信息
jstat -class <pid>          # 打印类加载统计
jstat -compiler <pid>       # 打印 JIT 编译统计

# 输出示例:
#  S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCU   YGC     YGCT    FGC    FGCT     GCT   
# 41984.0 41984.0  0.0   38592.0 335872.0 268288.0  629145.6   419430.4  97152.0 94034.7 12288.0 11526.3    156    8.432     5    3.456   11.888

# 3. jinfo - 查看/修改配置
jinfo <pid>                  # 打印所有配置
jinfo -flags <pid>          # 打印所有 JVM 参数
jinfo -sysprops <pid>       # 打印系统属性
jinfo -flag <name> <pid>    # 打印某个参数值
jinfo -flag +<flag> <pid>  # 启用参数(部分可动态修改)
jinfo -flag -<flag> <pid>   # 禁用参数

# 4. jmap - 内存映射
jmap -heap <pid>            # 打印堆配置和使用情况
jmap -histo <pid>          # 打印对象统计(按大小排序)
jmap -histo:live <pid>     # 先触发 Full GC 再统计(慎用)
jmap -dump:format=b,file=heap.hprof <pid>  # 导出堆转储
jmap -clstats <pid>        # 打印类加载器统计

# 5. jstack - 线程堆栈
jstack <pid>                # 打印线程堆栈
jstack -l <pid>            # 打印锁信息
jstack -F <pid>           # 强制打印(用于无响应情况)

# 6. jcmd - 多功能命令(JDK 7+)
jcmd <pid> VM.flags        # 打印 JVM 参数
jcmd <pid> GC.heap_info    # 打印堆信息
jcmd <pid> GC.class_histogram  # 对象统计
jcmd <pid> Thread.print    # 打印线程堆栈
jcmd <pid> VM.native_memory跟踪  # 本地内存跟踪

7.2 可视化工具

VisualVM
java 复制代码
# 启动 VisualVM
jvisualvm

# 功能:
# 1. CPU 性能分析
# 2. 内存分析(堆dump、对象统计)
# 3. 线程分析
# 4. GC 分析
# 5. 快照对比
JProfiler(商业)
XML 复制代码
# 启动 VisualVM
jvisualvm

# 功能:
# 1. CPU 性能分析
# 2. 内存分析(堆dump、对象统计)
# 3. 线程分析
# 4. GC 分析
# 5. 快照对比
Arthas(阿里开源)
java 复制代码
# 1. 下载安装
curl -O https://arthas.gitee.io/arthas-boot.jar
java -jar arthas-boot.jar

# 2. 常用命令
# 查看类信息
sc -d com.example.User

# 反编译类
jad com.example.User

# 查看方法调用
watch com.example.User methodName '{params, returnObj}'

# 追踪方法调用
trace com.example.User methodName

# 查看方法栈
stack com.example.User methodName

# 监控方法调用
monitor -c 5 com.example.User methodName

# 生成火焰图
profiler start
profiler stop --format html

# 线程分析
thread
thread -b   # 死锁检测

# 内存分析
heapdump /tmp/heap.hprof

# GC 分析
dashboard   # 实时面板
MAT(Memory Analyzer Tool)
java 复制代码
# 安装 MAT
# 下载:https:// eclipse.org/mat/downloads.php

# 导入堆转储文件
jmap -dump:format=b,file=heap.hprof <pid>

# 用 MAT 分析
# 1. Dominator Tree - 找出最大的对象树
# 2. Histogram - 对象直方图
# 3. Leak Suspects - 内存泄漏怀疑点
# 4. Top Consumers - 最大的对象

7.3 GC 日志分析工具

java 复制代码
# GCViewer
# https://github.com/chewiebug/GCViewer
java -jar gcviewer.jar gc.log

# GCEasy
# https://gceasy.io
# 在线分析 GC 日志

# GCPlot
# https://gcplot.com
# 开源 GC 日志分析

第八部分:OOM 全场景排查

8.1 OOM 类型与原因

java 复制代码
/**
 * OOM 场景分类
 */
public class OOMScenarios {
    
    // ============ 1. 堆内存溢出 (Heap OOM) ============
    public void heapOOM() {
        List<byte[]> list = new ArrayList<>();
        while (true) {
            list.add(new byte[1024 * 1024]);  // 1MB
        }
    }
    // 原因:对象无限创建、内存泄漏、堆太小
    // 解决:增大堆、优化代码、检查泄漏
    
    // ============ 2. 元空间溢出 (Metaspace OOM) ============
    // 原因:类加载过多(CGLib动态代理、OSGi、JSP编译)
    // 解决:增大元空间、优化类加载逻辑
    
    // ============ 3. 栈溢出 (StackOverflowError) ============
    public void stackOverflow() {
        stackOverflow();  // 无限递归
    }
    // 解决:修复递归逻辑、增大栈 (-Xss)
    
    // ============ 4. 栈内存耗尽 (Unable to create new native thread) ============
    // 原因:线程数过多
    // 解决:减少线程数、优化线程创建
    
    // ============ 5. 直接内存溢出 (DirectBuffer OOM) ============
    public void directMemoryOOM() {
        ByteBuffer buffer = ByteBuffer.allocateDirect(Integer.MAX_VALUE);
    }
    // 原因:NIO 使用过多
    // 解决:增大直接内存、检查 ByteBuffer 使用
}

8.2 OOM 排查实战

步骤1:配置 OOM 时自动导出堆
java 复制代码
# JVM 参数
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/heap.hprof \
-XX:+ExitOnOutOfMemoryError \
-XX:+CrashOnOutOfMemoryError
步骤2:使用 MAT 分析
java 复制代码
# 1. 下载 MAT: https://www.eclipse.org/mat/downloads.php

# 2. 打开堆转储文件
./MemoryAnalyzer heap.hprof

# 3. 关键分析功能:

# Histogram(直方图)
# - 按类名分组,查看对象数量和内存占用
# - 找出占用最大的类

# Dominator Tree(支配树)
# - 找出 GC Root 到对象的路径
# - 快速定位内存泄漏

# Leak Suspects(泄漏怀疑点)
# - MAT 自动分析可能的泄漏点

# Top Consumers(最大消费者)
# - 列出占用内存最大的对象
步骤3:使用 jmap + jhat 分析
java 复制代码
# 1. 导出堆
jmap -dump:format=b,file=heap.hprof <pid>

# 2. 使用 jhat 分析(内置工具,简单场景够用)
jhat heap.hprof

# 3. 浏览器访问 http://localhost:7000

# 4. 使用 OQL 查询
# select x from com.example.User x where x.age > 30
步骤4:使用 Arthas 分析
java 复制代码
# 1. 启动 Arthas
java -jar arthas-boot.jar

# 2. 生成堆转储
heapdump /tmp/heap.hprof

# 3. 分析大对象
sc -d com.example.HeavyObject

# 4. 查看对象创建
watch com.example.User <init> '{params, throwExp}' -x 3

8.3 常见 OOM 场景与解决方案

OOM 场景 原因 解决方案
集合类未清理 集合只增不减 及时清理、使用 WeakHashMap
静态集合持有引用 生命周期过长 避免 static 持有大对象
单例模式持有 Context Activity/Context 泄漏 使用 Application Context
监听器未注销 未移除监听器 onDestroy 中注销
Handler 内存泄漏 非静态内部类持有外部引用 使用静态内部类 + WeakReference
ThreadLocal 泄漏 线程池复用未清理 手动 remove()
资源未关闭 Stream、Connection 未关闭 try-with-resources
静态集合存储缓存 缓存无限增长 使用 LRU 缓存或 WeakReference
CGLib 代理过多 类加载器泄漏 限制动态代理使用
大量字符串拼接 StringBuilder 泄漏 及时 clear() 或限制大小

第九部分:全链路性能监控

9.1 APM 工具选型

工具 特点 开源 商业
SkyWalking 链路追踪、性能指标 -
Pinpoint 韩国开源,字节码注入 -
Zipkin Twitter 开源,轻量 -
Jaeger CNCF 项目,Go 实现 -
CAT 大众点评开源 -
New Relic 功能全面 -
Dynatrace AI 驱动 -
AppDynamics 应用性能 -

9.2 SkyWalking 实战

java 复制代码
# docker-compose.yml
version: '3'
services:
  oap:
    image: apache/skywalking-oap-server:9.5.0
    ports:
      - "11800:11800"  # gRPC
      - "12800:12800"  # HTTP
    environment:
      SW_STORAGE: elasticsearch
      SW_STORAGE_ES_HOSTS: elasticsearch:9200
  
  ui:
    image: apache/skywalking-ui:9.5.0
    ports:
      - "8080:8080"
    depends_on:
      - oap
    environment:
      SW_OAP_ADDRESS: oap:12800

  elasticsearch:
    image: elasticsearch:8.9.0
    ports:
      - "9200:9200"

9.3 Arthas 高级用法

java 复制代码
# 1. 火焰图 - 定位 CPU 热点
profiler start --duration 30
profiler stop --format html --file /tmp/flame.html

# 2. 方法执行耗时
trace com.example.UserService * '{methodName, #cost > 100}'

# 3. 监控方法调用
monitor -c 5 com.example.UserService addUser

# 4. 查看热点方法
jfr record --duration=60s
# 或使用 async-profiler

# 5. 类重编译(热部署)
retransform com.example.UserService

9.4 全链路排查流程

java 复制代码
问题发现 → 指标分析 → 链路追踪 → 定位根因 → 优化验证
   ↓          ↓           ↓           ↓           ↓
 监控告警    Grafana    SkyWalking   Arthas     验证优化

第十部分:完整的 JVM 深度学习体系!内容涵盖

运行时数据区 - 堆、栈、方法区完整解析

类加载子系统 - 5阶段流程、双亲委派、自定义加载器

垃圾回收机制 - 算法、收集器、分代策略

字节码与执行引擎 - 结构、指令、JIT优化

实战调优 - 参数配置、案例分析

工具链 - JDK工具、可视化工具、Arthas

性能监控 - 全链路监控方案

OOM排查 - 全场景分析

相关推荐
m0_702036531 小时前
html标签如何提升可访问性_aria-label与title区别【指南】
jvm·数据库·python
.ZGR.1 小时前
线程池相关知识及并发统计案例实现
java·开发语言
Mr_pyx1 小时前
面试题记录
jvm·数据结构·算法·spring·mybatis
ㄟ留恋さ寂寞1 小时前
Golang格式化输出占位符都有什么_Golang fmt占位符教程【通俗】
jvm·数据库·python
番茄去哪了1 小时前
JVM虚拟机基础篇(上)
jvm
慕言手记2 小时前
IDEA 插件常用-2026版
java·ide·spring boot·intellij-idea·idea·intellij idea
颖火虫盟主2 小时前
Hello World MCP Server 实现总结
java·前端·python
iiiiyu2 小时前
⾯向对象和集合编程题
java·大数据·开发语言·数据结构·编程语言
超級二蓋茨2 小时前
asp.net core中JwtBearerEvents中几个事件的生命周期
java·服务器·asp.net