深入浅出Java虚拟机(JVM)核心原理

目录

一、JVM概述

[1.1 大白话理解JVM](#1.1 大白话理解JVM)

[1.2 JVM架构](#1.2 JVM架构)

[1.3 跨平台运行的本质](#1.3 跨平台运行的本质)

二、类加载器

[1.1 类加载全过程](#1.1 类加载全过程)

[1.1.1 加载阶段](#1.1.1 加载阶段)

[1.1.2 验证阶段](#1.1.2 验证阶段)

[1.1.3 准备阶段](#1.1.3 准备阶段)

[2.2 双亲委派机制](#2.2 双亲委派机制)

[2.3 自定义类加载器](#2.3 自定义类加载器)

三、运行时数据区

[3.1 堆内存结构](#3.1 堆内存结构)

[3.1.1 新生代参数优化](#3.1.1 新生代参数优化)

[3.1.2 内存分配策略](#3.1.2 内存分配策略)

[3.2 虚拟机栈运行原理](#3.2 虚拟机栈运行原理)

[2.3 方法区演进](#2.3 方法区演进)

四、执行引擎深度解析

[4.1 解释器与JIT协作](#4.1 解释器与JIT协作)

[4.2 JIT优化技术](#4.2 JIT优化技术)

[4.2.1 方法内联优化](#4.2.1 方法内联优化)

[4.2.2 逃逸分析优化](#4.2.2 逃逸分析优化)

[4.3 AOT编译实践](#4.3 AOT编译实践)

五、垃圾回收全解析

[5.1 对象存活判定算法](#5.1 对象存活判定算法)

[5.1.1 可达性分析算法](#5.1.1 可达性分析算法)

[5.1.2 引用类型进阶](#5.1.2 引用类型进阶)

[5.2 经典GC算法对比](#5.2 经典GC算法对比)

[5.3 主流垃圾收集器](#5.3 主流垃圾收集器)

[5.3.1 Parallel收集器家族](#5.3.1 Parallel收集器家族)

[5.3.2 CMS收集器](#5.3.2 CMS收集器)

[5.3.3 G1收集器革新](#5.3.3 G1收集器革新)


一、JVM概述

1.1 大白话理解JVM

JVM是 Java Virtual Machine 的缩写,简单来说,JVM 就像是一台虚拟的小电脑,它能运行在 Windows、Linux 等各种各样的操作系统环境里。不过它可不直接和硬件打交道,而是和操作系统进行沟通交流,让操作系统帮忙去完成和硬件交互的那些事。

打个比方,我们平时用的真实电脑有 CPU 来处理数据、有内存来存储数据。JVM 这台 "小电脑" 也有类似的功能模块。它有自己的运行空间,里面划分出了不同的区域,就像真实电脑里的内存被划分成不同用途的区域一样。

当编写好 Java 程序后,java程序代码(.java)会被编译成一种 JVM 能理解的字节码文件(.class)。JVM 就负责运行这些字节码文件,它会把字节码翻译成计算机硬件能懂的指令,让程序在不同的操作系统上都能顺利运行。

而且,JVM 还能自动管理内存。比如,它会自动帮我们分配内存来存储程序运行时产生的数据,当这些数据不再使用时,JVM 还会自动把它们占用的内存回收掉,这样就不用担心内存不够用或者内存泄漏的问题。

所以说,JVM 就像是 Java 程序的一个 "大管家 ",为 Java 程序提供了一个统一的运行环境 ,让 Java 程序可以在不同的操作系统上都能稳定、高效地运行,实现了 "一次编写,到处运行" 的神奇效果。

1.2 JVM架构

**Java虚拟机(JVM)**是Java生态的核心引擎,通过将字节码转换为机器指令实现"一次编写,到处运行"。其架构包含三个核心子系统:

  • 类加载器:负责加载.class文件
  • 运行时数据区:内存管理核心区域
  • 执行引擎:包含解释器和JIT编译器
    JVM组件流程图

又比如我现在需要编译运行一个java程序 xiaoliang.java,那么这个java文件被运行的流程就如同下面这幅图:

总结

(1).java文件经过编译后变成 .class 字节码文件

(2)字节码文件通过类加载器 被搬运到 JVM 虚拟机中

(3)虚拟机主要的 5 大块:方法区,堆都为线程共享区域,有线程安全问题,栈和本地方法栈和计数器都是独享区域,不存在线程安全问题,而 JVM 的调优主要就是围绕堆,栈两大块进行

1.3 跨平台运行的本质

Java程序通过"中间层翻译"实现跨平台:

  • .java源码.class字节码(二进制中间格式)
  • JVM将字节码转换为本地机器指令(JIT编译优化)
  • 不同平台的JVM实现负责对接操作系统API

二、类加载器

关于类加载器的详细介绍,大家可以通过传送门查看我的这篇文章:

JVM 类加载器深度解析(含实战案例)-CSDN博客


1.1 类加载全过程

类加载生命周期

1.1.1 加载阶段
  • 通过全限定名获取二进制字节流
  • 生成方法区的运行时数据结构
  • 创建对应的Class对象(作为访问入口)
1.1.2 验证阶段
  • 文件格式验证(魔数CAFE BABE)
  • 元数据验证(继承关系检查)
  • 字节码验证(栈映射帧校验)
  • 符号引用验证(常量池检查)
1.1.3 准备阶段
  • 类变量(static变量)分配内存
  • 设置初始值(0/false/null等)
  • 示例:public static int value = 123; 此时value=0

2.2 双亲委派机制

双亲委派模型执行流程

破坏双亲委派的典型场景

  • SPI机制(JDBC驱动加载)
  • OSGi模块化
  • 热部署实现

2.3 自定义类加载器

java 复制代码
public class CustomClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) {
        byte[] classData = loadClassData(name);
        return defineClass(name, classData, 0, classData.length);
    }
    
    private byte[] loadClassData(String className) {
        // 自定义加载逻辑
    }
}

三、运行时数据区

3.1 堆内存结构

java 复制代码
// 堆内存分配示例
byte[] data = new byte[10 * 1024 * 1024]; // 10MB数组直接进入老年代
3.1.1 新生代参数优化
java 复制代码
-XX:NewRatio=2          # 老年代/新生代=2:1
-XX:SurvivorRatio=8     # Eden/Survivor=8:1:1
-XX:MaxTenuringThreshold=15 # 晋升阈值
3.1.2 内存分配策略
  • TLAB(Thread Local Allocation Buffer)
  • 逃逸分析与栈上分配
  • 大对象直接进入老年代

3.2 虚拟机栈运行原理

栈帧结构详解:

java 复制代码
public class StackFrameDemo {
    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        int c = add(a, b);
    }
    
    private static int add(int x, int y) {
        return x + y;
    }
}
操作步骤 局部变量表 操作数栈
iconst_1 [ ] [1]
istore_1 [a=1] [ ]
iconst_2 [a=1] [2]
istore_2 [a=1,b=2] [ ]
iload_1 [a=1,b=2] [1]
iload_2 [a=1,b=2] [1,2]
invoke [...] [...]

2.3 方法区演进

JDK版本 实现方式 参数配置 特点
≤1.6 永久代 -XX:PermSize=64m 受JVM内存限制
1.7 部分元数据 -XX:PermSize=64m 字符串常量池移至堆
≥1.8 元空间 -XX:MetaspaceSize=256m 使用本地内存,自动扩展

四、执行引擎深度解析

4.1 解释器与JIT协作

分层编译策略

  1. 第0层:纯解释执行
  2. 第1层:C1简单编译(方法调用计数触发)
  3. 第2层:C2深度优化(回边计数触发)

4.2 JIT优化技术

4.2.1 方法内联优化
java 复制代码
// 内联优化示例
public int add(int a, int b) {
    return a + b;
}

// 调用处被优化为直接相加
int result = x + y;
4.2.2 逃逸分析优化
  • 栈上分配(避免堆内存分配)
  • 锁消除(线程私有对象去锁)
  • 标量替换(分解对象为基本类型)

4.3 AOT编译实践

java 复制代码
# 使用GraalVM编译原生镜像
native-image -H:+PrintAnalysisCallTree \
             -H:+TraceClassInitialization \
             -jar app.jar

五、垃圾回收全解析

5.1 对象存活判定算法

5.1.1 可达性分析算法

GC Roots类型

  • 虚拟机栈局部变量
  • 方法区静态变量
  • 方法区常量引用
  • JNI全局引用
5.1.2 引用类型进阶
类型 回收条件 使用场景
强引用 永不回收 普通对象
软引用 内存不足时回收 缓存
弱引用 下次GC时回收 缓存/监听
虚引用 随时可能回收 堆外内存管理

5.2 经典GC算法对比

算法 优点 缺点 适用场景
标记-清除 简单快速 内存碎片 老年代
复制算法 无碎片 内存折半 新生代
标记-整理 内存紧凑 效率较低 老年代
分代收集 综合优势 实现复杂 现代JVM

5.3 主流垃圾收集器

5.3.1 Parallel收集器家族
  • Parallel Scavenge :吞吐量优先

  • Parallel Old :老年代并行整理

  • 参数配置示例:

    java 复制代码
    -XX:+UseParallelGC
    -XX:ParallelGCThreads=4
    -XX:MaxGCPauseMillis=200
5.3.2 CMS收集器
  • 四阶段过程

    1. 初始标记(STW)
    2. 并发标记
    3. 重新标记(STW)
    4. 并发清除
  • 内存碎片问题解决方案:

    java 复制代码
    -XX:+UseCMSCompactAtFullCollection
    -XX:CMSFullGCsBeforeCompaction=5
5.3.3 G1收集器革新
  • Region分区模型:将堆划分为1MB~32MB区域
  • SATB算法:Snapshot-At-The-Beginning
  • 最佳实践参数:
java 复制代码
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:InitiatingHeapOccupancyPercent=45



码字不易,希望可以一键三连,我们下期文章再见!!!

相关推荐
EPSDA3 分钟前
Linux线程池
linux·运维·服务器·开发语言·c++
虾球xz3 分钟前
游戏引擎学习第116天
java·学习·游戏引擎
大胖丫21 分钟前
vue 学习-vite api.js
开发语言·前端·javascript
遇见很ok23 分钟前
js中 ES6 新特性详解
开发语言·javascript·es6
没有晚不了安31 分钟前
1.13作业
开发语言·python
布谷歌35 分钟前
Oops! 更改field的数据类型,影响到rabbitmq消费了...(有关于Java序列化)
java·开发语言·分布式·rabbitmq·java-rabbitmq
PXM的算法星球37 分钟前
java(spring boot)实现向deepseek/GPT等模型的api发送请求/多轮对话(附源码)
java·gpt·microsoft
被程序耽误的胡先生40 分钟前
java中 kafka简单应用
java·开发语言·kafka
刀客12341 分钟前
python小项目编程-中级(1、图像处理)
开发语言·图像处理·python
卷卷的小趴菜学编程1 小时前
c++之多态
c语言·开发语言·c++·面试·visual studio code