JAVA虚拟机(JVM)

深入解析JAVA虚拟机(JVM)原理与优化

在这里插入图片描述

Java 平台无关性

编译与优化

JIT 优化

  • 逃逸分析
    • 若对象未逃逸出方法作用域,可能被优化为栈上分配(减少堆内存分配开销)。

AOT 编译

  • 提前编译
    • 在编译期将字节码直接编译为机器码,生成平台相关的二进制文件。

运行时内存组成

区域作用线程共享性
程序计数器记录当前线程执行的字节码指令地址(线程私有)。私有
Java 虚拟机栈存储局部变量表、操作数栈、动态链接等(线程私有)。私有
本地方法栈为本地(Native)方法服务(线程私有)。私有
存储对象实例,是垃圾回收(GC)的主要区域。共享
方法区存储已加载的类信息、常量、静态变量、JIT 编译后的代码等(Java 8 后为元空间)。共享

方法区实现方式

  • 永久代(Java 7 及之前)
  • 元空间(Java 8+,直接使用本地内存)
  • (部分实现可能使用堆内存)

Class 常量池

  • 作用:存储编译期生成的字面量符号引用
  • 加载流程:类加载时解析并存入运行时常量池

运行时常量池

  • 包含字面量符号引用,是类加载后的动态常量池。

字符串常量池

  • 特性:不可变,存储字符串字面量的唯一实例。
  • 来源
    • 字面量在编译期进入 Class 常量池,运行期进入 字符串常量池
    • 运行期通过 String.intern() 手动添加字符串到池中。
  • intern() 方法逻辑
    • 若池中已存在相同字符串,返回池中的引用;否则将字符串加入池并返回新引用。

对象存储模型(OOP-Klass)

对象内存布局

  1. 对象头
    • 存储 MarkWord(锁状态)、Klass 指针(指向类元数据)等。
  2. 实例数据
    • 对象的字段值(包括父类字段)。
  3. 对齐填充
    • 填充字节以保证内存对齐(提升访问效率)。

对象创建流程

  1. 类加载检查
    • 检查常量池是否能定位到类的符号引用,是否已加载。
  2. 内存分配
    • 指针碰撞法(内存规整时)或 空闲列表法(内存不规整时)。
  3. 初始化零值
    • 将对象字段初始化为默认值(如 int0,引用为 null)。
  4. 设置对象头
    • 填充 MarkWord 和 Klass 指针。
  5. 执行构造方法
    • 调用 <init> 方法初始化对象。
  6. 返回对象引用

TLAB 技术(Thread-Local Allocation Buffer)

  • 作用:为每个线程分配独立的堆内存区域(TLAB),避免多线程分配时的竞争。
  • 替代方案:CAS + 失败重试(性能较低)。

垃圾回收(GC)

算法分类

算法特点缺点
标记-清除直接标记并清除垃圾对象。内存碎片化
标记-复制将内存分为两块,存活对象复制到另一块,清空原块。浪费 50% 内存
标记-整理标记存活对象并移动到内存一端,整理碎片。耗时较长

GC 的 STW(Stop-The-World)

  • 问题:垃圾回收时会暂停所有应用线程。
  • 优化:通过并发标记、三色标记法减少 STW 时间。

对象存活判断

  • 引用计数法
    • 缺点:无法处理循环引用(如两个对象互相引用但无外部引用)。
  • 可达性分析法
    • GC Roots
      • 系统类加载器加载的类。
      • 活着的线程栈中的局部变量。
      • synchronized 锁定的对象。
      • JNI 引用的对象。
    • 从 GC Roots 出发,遍历引用链,不可达的对象判定为可回收。

三色标记法

  • 阶段
    1. 初始标记(STW):标记 GC Roots 直接引用的对象。
    2. 并发标记:从灰色对象(已标记但未扫描子引用)开始遍历,与用户线程并行。
    3. 重新标记(STW):修正并发标记期间变动的引用(如新增或删除引用)。
  • 漏标问题解决
    • 增量更新:记录并发期间新增的引用。
    • 原始快照:基于标记开始时的引用关系快照。

引用类型

类型回收时机典型应用
强引用默认引用类型,只要存在强引用,对象不会被回收。普通对象赋值
软引用内存不足时优先回收软引用指向的对象。缓存实现
弱引用只能存活到下一次 GC 前,无论内存是否充足。ThreadLocal
虚引用无法通过虚引用获取对象实例,仅用于跟踪对象被回收的状态(需配合 ReferenceQueue)。对象回收状态监控

垃圾回收器分类

按线程模型

  • 串行单线程
    • Serial GC(新生代)、Serial Old(老年代)。
  • 并行多线程(关注吞吐量)
    • Parallel New(新生代)、Parallel Old(老年代)。
  • 并发(关注停顿时间)
    • CMS(老年代,标记-清除算法)。
    • G1(整堆,标记-复制 + 标记-整理)。
    • ZGC(整堆,低停顿、高吞吐)。

CMS 回收流程

  1. 初始标记(STW):标记 GC Roots 直接关联的对象。
  2. 并发标记:与用户线程并行标记存活对象。
  3. 重新标记(STW):修正并发标记期间的变动。
  4. 并发清除:与用户线程并行清理垃圾对象。
  5. Full GC(单线程):当碎片过多或老年代不足时触发。

G1 回收流程

  1. Young GC(STW):回收 Eden + Survivor 区。
  2. 并发标记:标记存活对象。
  3. Mixed GC(STW):回收部分老年代 + 新生代。
  4. Full GC(单线程,尽量避免):内存不足时触发。
  • 特点:将堆划分为多个 Region,动态调整新生代/老年代比例。

新生代与老年代

内存划分

  • 新生代:存放新分配的对象,分为 Eden + 2 个 Survivor 区(S0/S1)。
  • 老年代:存放长期存活的对象(如经过多次 GC 仍存活的对象)。

Java 8 GC 行为

  • 新生代:标记-复制算法。
  • 老年代:标记-整理算法。
  • 对象分配流程
    1. 新对象优先分配到 Eden
    2. Eden 快满时触发 Young GC
      • 扫描 EdenSurvivor0,存活对象复制到 Survivor1
      • 清空 EdenSurvivor0
    3. 下次 GC 时交换 Survivor0Survivor1 角色。
  • 跨代引用问题
    • Remembered Set:记录老年代对象指向新生代对象的引用,避免全堆扫描。

类生命周期

加载阶段

  1. 加载:将 .class 文件读入 JVM。
  2. 链接
    • 验证:确保字节码合法。
    • 准备:为静态变量分配内存并赋默认值。
    • 解析:将符号引用转换为直接引用。
  3. 初始化:执行静态变量赋值和静态代码块(<clinit> 方法)。

使用与卸载

  • 使用:类实例化、调用方法等。
  • 卸载:满足条件时由 JVM 卸载类(如无实例、无引用)。

懒加载

  • 触发条件
    • 创建类的实例。
    • 访问类的静态变量或方法。
    • 反射调用类。
  • 基础类:JVM 启动时自动加载(如 java.lang.Object)。

类加载器

分类

  • 启动类加载器(C++ 实现,JVM 内部组件)。
  • 其他类加载器(Java 实现,独立于 JVM)。

双亲委派模型

  1. 类加载请求先委派给父类加载器。
  2. 父类加载器无法加载时,子类加载器尝试加载。
  • 目的:避免重复加载,保证核心类安全(如防止用户篡改 java.lang.String)。

JVM 调优工具与参数

工具

  • 命令行工具
    • jps:查看 JVM 进程状态。
    • jstack:分析线程堆栈(死锁检测)。
    • jmap:生成堆转储快照(分析内存泄漏)。
    • jstat:监控 JVM 统计信息(GC、类加载等)。
  • 可视化工具
    • jconsoleVisualVM:图形化监控堆内存、线程、GC 等。

关键参数

类别参数示例作用
堆内存-Xms(初始堆大小)、-Xmx(最大堆大小)控制堆内存范围
栈内存-Xss(线程栈大小)设置每个线程的栈容量
GC 选择-XX:+UseG1GC(启用 G1)、-XX:+UseParallelGC(启用并行 GC)指定垃圾回收器
元空间-XX:MetaspaceSize(初始大小)、-XX:MaxMetaspaceSize(最大大小)替代永久代的内存管理
调试信息-XX:+PrintGCDetails(打印 GC 详情)、-verbose:gc(输出 GC 日志)记录 GC 行为
OOM 处理-XX:+HeapDumpOnOutOfMemoryError(内存溢出时生成堆转储)辅助分析内存泄漏
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值