JVM简介与入门

发布时间:2026/11/3 15:07:22

JVM简介与入门 文章目录1.JVM内存区域1.1 代码演示1.1.1 基本类型内存演变1.1.2 对象类型内存演变2.GC算法与过程2.1 GC Roots 与可达性分析2.2 回收算法2.2.1 标记清除2.2.2 标记整理2.2.3 复制算法2.3 经典分代 GC 过程3.垃圾收集器3.1 常见垃圾收集器3.2 Serial 收集器3.3 Parallel 收集器3.4 CMS 收集器3.4.1 CMS执行过程1.初始标记2.并发标记3.重新标记4.并发清除3.5 G1垃圾收集器3.5.1 Region3.5.2 为什么叫Garbage First3.5.3 G1的Young GC3.5.4 G1并发标记3.5.5 Mixed GC3.5.6 Full GC3.5.7 G1停顿时间目标3.6 ZGC垃圾收集器3.7 垃圾收集器如何选择3.8 通过代码观察GC3.8.1 指定垃圾收集器3.8.2 查看当前垃圾收集器3.8.3 JDK8查看GC日志3.8.4 JDK9及以上查看GC日志3.8.5 GC日志主要看什么3.9 常见垃圾收集器总结1.JVM内存区域网上随手抓的两张图下图为较早期 HotSpot JVM 内存结构示意其中混合了运行时数据区和具体垃圾收集器的堆结构仅用于建立整体概念。区域与代码对应关系图1.1 代码演示1.1.1 基本类型内存演变执行以下代码最后会输出10publicclassTestJvm{publicstaticvoidmain(String[]args){inta10;newTestJvm().add(a);System.out.println(a);}publicvoidadd(inta){intb10;System.out.println(ab);a11;}}演变过程1.1.2 对象类型内存演变publicclassTestJvm{staticIntegerc5;publicstaticvoidmain(String[]args){inta10;newTestJvm().add(a);System.out.println(a);}publicvoidadd(inta){intb10;PersonpersonnewPerson();person.id1;person.namelisi;System.out.println(ab);a11;}}classPerson{intid;Stringname;}测试引用类型参数传递本质仍是值传递实验 1修改对象内容实验 2修改形参指向2.GC算法与过程2.1 GC Roots 与可达性分析JVM 判断对象是否存活时通常采用可达性分析。从一组称为 GC Roots 的对象或引用出发向下搜索能够从 GC Roots 到达的对象认为仍然存活无法从任何 GC Roots 到达的对象才成为垃圾回收候选。常见 GC Roots 包括当前线程栈帧局部变量引用的对象、类静态字段引用的对象、JNI 引用以及 JVM 内部的一些引用等。2.2 回收算法常见的基础垃圾回收算法主要有三种标记-清除、标记-整理、复制算法。2.2.1 标记清除2.2.2 标记整理2.2.3 复制算法经典复制算法将内存划分为两个区域每次主要使用其中一个区域。GC 时只将存活对象复制到另一区域并连续排列随后原区域整体重新变为空闲。其优点是不产生内存碎片缺点是需要预留额外空间并存在对象复制开销。2.3 经典分代 GC 过程接下来以传统分代堆结构为例说明 Eden、Survivor 和 Old 之间的对象流转。不同垃圾收集器的实际堆布局和回收过程有所不同例如 G1 采用 Region 化管理并非固定连续的 Eden、S0、S1、Old 布局。实际的 GC 过程比较复杂不同垃圾收集器会综合运用复制、标记-整理等不同回收思想。在经典分代模型中Young GC 主要回收年轻代Full GC 通常涉及整个堆或更大范围的内存回收不能简单理解为“老年代 GC”。不同垃圾收集器还可能存在其他回收类型例如 G1 中的 Mixed GC。经典教学模型通常将年轻代画成 Eden、S0、S1并以 Eden:S0:S1≈8:1:1 帮助理解复制过程S0 和 S1 轮流作为 Survivor 区。需要注意这只是经典示意实际比例以及年轻代、老年代大小会受到 JVM 版本、垃圾收集器和运行时参数影响并不是固定不变的默认布局。初始空间如下图首次GC第二次GC对象每经过一次 Young GC 并继续存活年龄通常会增加。当达到晋升年龄阈值或满足其他晋升条件时会进入老年代。实际晋升年龄并不一定固定为某个数字。3.垃圾收集器前面介绍的标记-清除、标记-整理、复制算法属于垃圾回收的基础算法而垃圾收集器Garbage Collector则是这些算法在 JVM 中的具体实现。不同垃圾收集器追求的目标并不完全相同主要需要在以下几个方面进行权衡吞吐量Throughput程序运行过程中真正用于执行用户代码的时间占比。停顿时间Pause Time垃圾回收导致应用线程暂停的时间。内存占用Footprint垃圾收集器自身以及运行过程中需要消耗的额外内存。通常不存在一个垃圾收集器在所有场景下都是最优的需要根据具体业务进行选择。早期 HotSpot 中年轻代和老年代经常使用不同的垃圾收集器组合例如Serial Serial Old Parallel Scavenge Parallel Old ParNew CMS而现代常用的 G1、ZGC 等垃圾收集器内部结构已经更加复杂不再适合简单理解为“年轻代一个收集器 老年代一个收集器”。3.1 常见垃圾收集器垃圾收集器主要目标主要特点当前定位Serial简单、低额外开销单线程垃圾回收小堆、资源有限场景Parallel高吞吐量多线程并行回收吞吐量优先CMS低停顿并发标记清除历史重要JDK14 已移除G1吞吐量与停顿平衡Region、并发标记、增量回收现代 HotSpot 主流ZGC极低停顿大量 GC 工作与应用线程并发执行延迟敏感场景CMS 虽然已经被移除但历史上使用非常广泛而且仍然是 JVM 面试中比较常见的知识点因此这里仍然保留介绍。3.2 Serial 收集器Serial 是比较简单的垃圾收集器。发生垃圾回收时只使用一个 GC 线程执行垃圾回收并暂停其他应用线程。也就是会产生Stop-The-WorldSTW可以通过下面参数指定 Serial GC-XX:UseSerialGC简单理解应用线程──────┐ ┌────── │ 暂停 │ GC线程 └── GC ─────┘Serial 的主要特点单线程执行垃圾回收实现简单没有多 GC 线程之间的同步开销GC 时需要暂停应用线程因此 Serial 并不是简单理解为“性能差所以没人使用”。对于堆内存较小CPU 核心数较少小型工具程序对资源占用比较敏感等场景Serial 仍然可能是合适的选择。3.3 Parallel 收集器Parallel Collector 的核心目标是尽可能提高系统整体吞吐量。可以通过-XX:UseParallelGC启用。Serial GCGC线程1 ─────────────→Parallel GCGC线程1 ──────→ GC线程2 ──────→ GC线程3 ──────→ GC线程4 ──────→多个 GC 线程同时进行垃圾回收因此在多核 CPU 环境中能够更快完成一次 GC。但需要注意Parallel 虽然垃圾回收速度快但 GC 期间应用线程仍然需要暂停。因此它更关注整体吞吐量而不是单次 GC 停顿尽可能短适合的典型场景后台计算任务批处理大数据计算对吞吐量要求高对偶尔出现较长停顿不敏感3.4 CMS 收集器CMS 全称Concurrent Mark Sweep即并发标记清除垃圾收集器。CMS 的主要设计目标是尽可能降低垃圾回收导致的应用停顿时间。CMS 曾经是非常重要的低停顿垃圾收集器。需要注意JDK 9CMS 被标记为废弃 JDK 14CMS 正式被移除因此现代项目已经不会再选择 CMS但它对理解低停顿垃圾收集器以及 G1 的发展仍然很有帮助。3.4.1 CMS执行过程CMS 的主要过程可以简化为初始标记 ↓ 并发标记 ↓ 重新标记 ↓ 并发清除1.初始标记从 GC Roots 出发快速标记与 GC Roots 直接关联的对象。这一阶段需要 STW但由于处理的数据相对较少因此通常持续时间比较短。2.并发标记继续沿对象引用关系进行可达性分析。此时GC线程 → 进行对象标记 应用线程 → 继续运行两者可以同时进行。这也是 CMS 中Concurrent的主要体现。3.重新标记由于并发标记阶段应用线程仍然在继续运行对象之间的引用关系可能发生变化。因此需要再次暂停应用线程对并发标记阶段产生变化的引用关系进行修正。这一阶段需要 STW4.并发清除清除已经确定为垃圾的对象。这一阶段可以与应用线程并发执行。CMS 使用的是标记-清除Mark-Sweep思想因此一个明显的问题就是容易产生内存碎片。另外在并发标记和并发清除期间应用线程仍然会继续创建和修改对象。因此可能产生浮动垃圾Floating Garbage即本轮 GC 过程中刚刚变成垃圾但没有来得及被本轮 GC 回收的对象需要等待下一轮 GC。CMS 可以简单总结为优点 低停顿 缺点 并发阶段占用 CPU 可能产生浮动垃圾 标记-清除可能产生内存碎片3.5 G1垃圾收集器G1 全称Garbage First Garbage CollectorG1 是现代 HotSpot 中非常重要的垃圾收集器。从 JDK 9 开始G1 成为 Server 模式下默认的垃圾收集器。G1 的主要目标是在保持较高吞吐量的同时尽可能控制垃圾回收停顿时间。前面介绍经典分代模型时我们画的是Young Generation ┌───────────────┐ │ Eden │ S0 │ S1│ └───────────────┘ Old Generation ┌───────────────┐ │ Old │ └───────────────┘这种图很适合理解传统分代模型。但是 G1 的内存布局发生了比较大的变化。3.5.1 RegionG1 将整个 Java 堆划分为大量大小相同的Region可以简单理解为┌────┬────┬────┬────┬────┬────┐ │ E │ O │ S │ E │ O │ E │ ├────┼────┼────┼────┼────┼────┤ │ O │ E │ O │ S │ O │ E │ └────┴────┴────┴────┴────┴────┘其中E Eden Region S Survivor Region O Old Region另外还有用于存放大对象的Humongous Region所以需要特别注意G1 仍然存在年轻代和老年代的逻辑概念。只是这些区域物理内存上不要求连续也就是说传统理解 Eden | Survivor | Old通常画成几个连续的大块。而 G1 中Eden Region Survivor Region Old Region可以散布在整个堆中。3.5.2 为什么叫Garbage FirstG1 会统计不同 Region 中垃圾对象的数量 可回收空间 回收成本并优先选择垃圾较多、回收收益比较高的 Region。也就是Garbage First垃圾优先。因此 G1 不一定每次都把整个老年代全部处理一遍而是可以优先处理收益较高的一部分 Region。3.5.3 G1的Young GC正常情况下新对象主要分配在Eden RegionEden 空间不足时会触发Young GC存活对象可能Eden ↓ Survivor或者满足晋升条件后Eden / Survivor ↓ Old因此G1 虽然使用 Region 管理堆但对象的年轻代存活、年龄增加和晋升老年代等思想依然存在。3.5.4 G1并发标记随着 Old Region 越来越多G1 会在满足一定条件后启动并发标记。简化理解Young GC ↓ Concurrent Start ↓ 并发标记 ↓ Remark ↓ Cleanup其中大部分标记工作可以GC线程 应用线程并发运行从而减少长时间 STW。3.5.5 Mixed GC完成并发标记后G1 已经知道不同 Old Region 中大概有多少垃圾。随后可以进行Mixed GCMixed GC 与普通 Young GC 最大的区别是Young GC 主要回收年轻代 Region Mixed GC 年轻代 Region 部分 Old Region因此Mixed GC 可以同时回收年轻代和部分老年代。这也是 G1 非常重要的一个特点。3.5.6 Full GC如果正常的 Young GC、并发标记和 Mixed GC 无法及时腾出足够空间G1 可能退化为Full GCFull GC 通常涉及更大范围的堆内存回收并且会产生较长时间的 STW。所以需要注意Young GC ↓ Mixed GC属于 G1 正常运行过程中的重要组成部分。而Full GC更多可以理解为正常回收无法满足内存需求后的兜底手段。因此线上如果出现频繁 Full GC通常就值得进行进一步排查。3.5.7 G1停顿时间目标G1 可以设置期望的最大 GC 停顿目标-XX:MaxGCPauseMillis200默认目标通常为200ms但需要注意200ms 是 JVM 尽量达到的目标而不是绝对保证。G1 会根据之前的 GC 情况动态调整Young Generation 大小 本次回收多少 Region尽可能接近设定的停顿目标。因此-XX:MaxGCPauseMillis更准确的理解应该是GC 停顿时间期望值。而不是GC 一定不能超过这个时间。3.6 ZGC垃圾收集器ZGC 是面向极低停顿时间设计的垃圾收集器。可以通过-XX:UseZGC启用。ZGC 的一个重要特点是将大量耗时的垃圾回收工作与应用线程并发执行。因此能够尽可能缩短Stop-The-World时间。可以简单对比Parallel GC 重点 高吞吐量 G1 重点 吞吐量与停顿时间之间取得平衡 ZGC 重点 极低延迟ZGC 比较适合堆内存较大在线服务对响应时间非常敏感无法接受长时间 GC 停顿的场景。当然低延迟通常也需要付出一定 CPU、内存以及垃圾回收复杂度方面的代价。因此也不能理解成ZGC 一定比 G1 好最终仍然需要结合实际业务进行选择。3.7 垃圾收集器如何选择可以先通过下面的方式帮助记忆场景可以优先考虑堆较小、程序简单Serial追求高吞吐量Parallel普通服务端 Java 应用G1对低延迟要求非常高ZGCCMS历史学习、面试但生产环境中不能只根据这张表直接决定垃圾收集器。真正选择时还应该关注堆大小 CPU核心数 Young GC频率 Full GC频率 GC停顿时间 系统吞吐量 业务允许的最大延迟如果系统运行本身没有明显 GC 问题通常没有必要一开始就进行大量 JVM 参数调优。只有当 GC 指标明显不满足业务需求时再结合 GC 日志进行针对性优化。3.8 通过代码观察GC垃圾收集器只看概念比较抽象可以通过简单代码实际制造对象分配并观察 GC 日志。下面写一个简单测试importjava.util.ArrayList;importjava.util.List;publicclassGcTest{publicstaticvoidmain(String[]args)throwsException{Listbyte[]listnewArrayList();for(inti0;i200;i){// 每次分配约256KB对象list.add(newbyte[256*1024]);// 保留一部分对象后再释放引用if(list.size()20){list.clear();}Thread.sleep(20);}}}这里不断创建byte[]对象。为了更容易触发 GC可以把 Java 堆设置得小一些。例如-Xms32m -Xmx32m3.8.1 指定垃圾收集器Serial-XX:UseSerialGCParallel-XX:UseParallelGCG1-XX:UseG1GCZGC-XX:UseZGC不同 JDK 版本支持的垃圾收集器有所不同。例如 CMS 在现代 JDK 中已经不存在因此-XX:UseConcMarkSweepGC只适用于历史版本。3.8.2 查看当前垃圾收集器可以执行java-XX:PrintCommandLineFlags-version观察 JVM 最终使用的参数。如果看到-XX:UseG1GC就说明当前使用的是 G1。3.8.3 JDK8查看GC日志因为本文前面的部分测试使用的是 JDK8所以如果继续使用 JDK8可以添加-XX:PrintGCDetails -XX:PrintGCTimeStamps例如-Xms32m -Xmx32m -XX:UseParallelGC -XX:PrintGCDetails -XX:PrintGCTimeStamps运行后可以在控制台看到 GC 信息。3.8.4 JDK9及以上查看GC日志从 JDK9 开始JVM 使用统一日志系统。可以使用-Xlog:gc*例如java-Xms32m-Xmx32m-XX:UseG1GC-Xlog:gc* GcTest运行后可能看到Pause Young Concurrent Mark Remark Cleanup等 GC 信息。3.8.5 GC日志主要看什么刚开始学习 GC 日志时不需要马上分析每一个字段。可以先重点关注发生了什么类型的GC GC前占用了多少内存 GC后剩余多少内存 堆总容量 本次GC耗时 Young GC是否过于频繁 Full GC是否频繁例如看到100M - 30M (256M)可以简单理解为GC前 100MB GC后 30MB 当前堆容量 256MB如果线上出现Young GC非常频繁 Full GC频繁 单次GC停顿突然很长 GC完成后内存仍然降不下来就需要进一步排查对象创建速度是否过快 是否存在大量长期存活对象 是否存在内存泄漏 堆大小是否设置合理 是否存在大量大对象 垃圾收集器是否适合当前业务后续可以再结合jstat jmap jstack Arthas GC日志分析工具进一步分析 JVM 状态。3.9 常见垃圾收集器总结收集器GC方式主要目标主要特点Serial单线程简单小堆、资源较少Parallel多线程并行高吞吐量GC速度快但停顿可能较长CMS并发低停顿历史收集器会产生碎片G1并行 并发平衡吞吐量和停顿Region、Young GC、Mixed GCZGC高度并发极低延迟大部分GC工作与应用线程并发可以简单记忆Serial ↓ 简单 Parallel ↓ 吞吐量 CMS ↓ 低停顿历史 G1 ↓ 吞吐量 停顿平衡 ZGC ↓ 极低延迟实际开发中最重要的并不是把每一个垃圾收集器的所有阶段全部背下来而是首先理解为什么要有不同垃圾收集器 不同收集器解决什么问题 吞吐量和停顿时间之间如何权衡 Young GC、Mixed GC、Full GC分别代表什么 发生频繁Full GC时应该如何排查理解这些核心问题以后再继续深入垃圾收集器的具体实现会容易很多。
延伸阅读

更多相关文章

2026/10/26 22:47:21

微信小程序超市系统源码深度解析:从库存并发到支付闭环

简介:这是一套高完成度的微信小程序超市购物系统毕设项目源码,面向计算机、电子信息工程等专业本科生,解决毕业设计选题难、实战经验缺、代码调试繁三大痛点,亦适用于课程设计与期末大作业开发。资源包共1609个文件,涵…

2026/10/24 17:47:37

CUDA编程:配置开发环境

相关概念 GPU: GPU是硬件,又称独立显卡,需要安装显卡驱动(GPU Driver)才能使用。安装显卡驱动时,默认安装CUDA 驱动运行时库(CUDA Driver Runtime),这是供软件调用 GPU…

2026/11/3 15:04:30

Kotlin数据流:冷流与热流在Android开发中的应用

1. Kotlin 数据流基础概念解析在 Android 开发中,数据流(Flow)已经成为处理异步数据序列的首选方案。与传统的回调或 RxJava 相比,Kotlin 数据流基于协程构建,提供了更符合直觉的编程模型。想象一下数据流就像一条传送…

2026/11/3 15:04:30

Paperclip:轻量级 AI Agent 框架,面向生产环境的 Node.js 实现

1. 项目概述:Paperclip 不是回形针,而是一个被低估的 AI Agent 开发范式“Paperclip”这个词在开发者社区里最近频繁冒头,但和办公用品毫无关系——它指的是一套轻量、可嵌入、面向实际业务流的 AI Agent 构建工具链。我第一次在 GitHub 上看…

2026/11/3 15:04:30

STM32嵌入式LVGL图形库移植与UI开发实战指南

简介:这份《LVGL开发指南》中文V1.3版是正点原子面向STM32嵌入式开发者编写的图形界面开发教程,适合已具备单片机基础、希望在资源受限的微控制器上实现触摸UI的工程师与学生。全书按基础篇展开,涵盖LVGL的组件体系与移植条件、源码获取、无操…

2026/11/3 15:04:30

Prim与Kruskal算法:最小生成树实战解析

1. 项目背景与核心概念 这个标题看起来像是某个算法学习或编程挑战系列中的一篇,Day57可能代表连续学习的第57天。从标题中的"寻宝"、"prim算法"和"kruskal算法"可以判断,这应该是一道关于图论中最小生成树(MST)问题的编程…

2026/11/3 15:04:30

决策树算法原理与应用:从基础到实践

1. 决策树算法概述:从生活决策到机器学习的桥梁决策树算法本质上是对人类日常决策过程的数学建模。想象一下你去超市买水果的场景:首先看价格是否合适(根节点),如果价格合适再检查新鲜度(内部节点&#xff…

2026/11/3 14:59:28

大模型推理优化实战:vLLM与TensorRT-LLM在RTX 4060 Laptop上的落地对比

1. 项目概述:Model-Optimizer不是工具名,而是一类工程实践的统称“Model-Optimizer”这个词在当前AI工程圈里,已经悄然脱离了字面意义——它不再指代某个具体叫这个名字的开源库或商业软件,而是演变成一种面向生产环境的大模型推理…

2026/11/3 7:47:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/3 14:24:53

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/3 1:04:48

STM32实战:基于MAX14431数字隔离器的SPI隔离通信方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/3 1:04:48

工业SPI隔离通信设计:MAX14431BAEE+与STM32L4A6RG实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/3 1:04:48

STM32+NRF24L01无线抢答器:硬件设计、通信协议与低功耗实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/3 7:47:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/3 14:24:53

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑