JVM初学者指南:Java虚拟机基础知识全解析
摘要:本文记录了Java虚拟机(JVM)的基本概念、架构、内存模型及工作原理的相关笔记 - lenyan。
一、JVM简介
1.1 什么是JVM?
JVM(Java Virtual Machine,Java虚拟机)是运行Java字节码的虚拟机。JVM是Java"一次编写,到处运行"这一特性的关键所在。无论什么平台,只要安装了对应的JVM,就能运行Java程序。
JVM有以下核心特点:
- 跨平台性:Java代码被编译成字节码后,可以在任何安装了JVM的设备上运行
- 自动内存管理:通过垃圾回收机制自动释放不再使用的内存
- 安全性:提供沙箱安全机制,限制Java程序访问本地系统资源
1.2 JVM在Java体系中的位置
Java体系结构分为三个主要部分:
- Java编程语言:开发人员使用的高级编程语言
- Java虚拟机(JVM):运行Java字节码的虚拟机
- Java API(类库):Java提供的标准类库
二、JVM架构详解
2.1 JVM整体架构
JVM主要由以下部分组成:
- 类加载子系统(Class Loader Subsystem)
- 运行时数据区(Runtime Data Area)
- 执行引擎(Execution Engine)
- 本地方法接口(Native Method Interface)
- 本地方法库(Native Method Library)
2.2 类加载子系统
类加载子系统负责加载、链接和初始化类文件。类加载过程分为三个阶段:
-
加载(Loading):
- 通过类的全限定名获取该类的二进制字节流
- 将字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成一个代表该类的java.lang.Class对象
-
链接(Linking):
- 验证(Verification):确保字节码符合JVM规范,不会危害虚拟机安全
- 准备(Preparation):为类的静态变量分配内存并设置初始值
- 解析(Resolution):将常量池中的符号引用替换为直接引用
-
初始化(Initialization):
- 执行类构造器()方法,为类的静态变量赋予正确的初始值
类加载器主要有四种:
- 启动类加载器(Bootstrap ClassLoader):加载Java核心类库
- 扩展类加载器(Extension ClassLoader):加载扩展类库
- 应用类加载器(Application ClassLoader):加载应用程序类路径下的类
- 自定义类加载器(User-Defined ClassLoader):用户自定义的类加载器
2.3 JVM内存结构
JVM运行时数据区主要包括:
-
方法区(Method Area):
- 存储已被虚拟机加载的类信息、常量、静态变量等
- JDK8之前称为永久代(PermGen),JDK8及以后称为元空间(Metaspace)
-
堆(Heap):
- 存储对象实例及数组
- 所有线程共享
- 垃圾收集器管理的主要区域
-
Java虚拟机栈(JVM Stack):
- 线程私有
- 每个方法执行时会创建一个栈帧(Stack Frame)
- 栈帧包含局部变量表、操作数栈、动态链接、方法出口等信息
-
本地方法栈(Native Method Stack):
- 为本地方法(Native Method)服务
- 类似于Java虚拟机栈
-
程序计数器(Program Counter Register):
- 线程私有,占用很小的内存空间
- 指示当前线程所执行的字节码指令的地址
三、垃圾回收机制
3.1 垃圾回收算法
-
标记-清除算法(Mark-Sweep):
- 标记阶段:标记出所有需要回收的对象
- 清除阶段:统一回收被标记的对象
- 缺点:效率低,产生内存碎片
-
复制算法(Copying):
- 将内存分为相等的两块,每次只使用其中一块
- 当这一块内存用完,就将存活对象复制到另一块
- 优点:效率高,没有内存碎片
- 缺点:内存利用率低
-
标记-整理算法(Mark-Compact):
- 标记阶段:与标记-清除算法一样
- 整理阶段:将存活对象移到内存的一端,清理边界以外的内存
- 优点:不产生内存碎片
- 缺点:移动对象需要成本
-
分代收集算法(Generational Collection):
- 根据对象存活周期的不同,将内存分为几块
- 一般将堆分为新生代和老年代
- 新生代采用复制算法,老年代采用标记-整理算法
3.2 垃圾回收器
常见的垃圾回收器包括:
- Serial收集器:单线程收集器,简单高效
- ParNew收集器:Serial收集器的多线程版本
- Parallel Scavenge收集器:关注吞吐量的收集器
- Serial Old收集器:Serial收集器的老年代版本
- Parallel Old收集器:Parallel Scavenge收集器的老年代版本
- CMS收集器:关注最短停顿时间的收集器
- G1收集器:区域化分代式的收集器,可预测停顿时间
- ZGC收集器(Java 11+):低延迟垃圾收集器
- Shenandoah收集器:与ZGC类似的低延迟收集器
四、JVM调优基础
4.1 常用JVM参数
-
堆内存相关:
-Xms
:设置堆的初始大小-Xmx
:设置堆的最大大小-Xmn
:设置新生代大小
-
垃圾回收相关:
-XX:+UseSerialGC
:使用Serial & Serial Old收集器-XX:+UseParallelGC
:使用Parallel Scavenge & Parallel Old收集器-XX:+UseConcMarkSweepGC
:使用ParNew & CMS收集器-XX:+UseG1GC
:使用G1收集器
-
类加载相关:
-XX:PermSize
:方法区初始大小(JDK8前)-XX:MaxPermSize
:方法区最大大小(JDK8前)-XX:MetaspaceSize
:元空间初始大小(JDK8+)-XX:MaxMetaspaceSize
:元空间最大大小(JDK8+)
4.2 JVM监控与分析工具
-
命令行工具:
jps
:显示当前所有Java进程的PIDjstat
:监视虚拟机各种运行状态信息jinfo
:实时查看和调整虚拟机参数jmap
:生成堆转储快照jhat
:分析堆转储快照jstack
:生成虚拟机当前线程快照
-
可视化工具:
- JConsole:Java监视与管理控制台
- VisualVM:多合一故障处理工具
- JMC(Java Mission Control):Java任务控制工具
- MAT(Memory Analyzer Tool):内存分析工具
- Arthas:阿里开源的Java诊断工具
五、JVM实战问题解析
5.1 常见JVM异常及解决方案
-
OutOfMemoryError: Java heap space
- 原因:堆内存不足
- 解决方案:增加堆内存大小,检查内存泄漏
-
OutOfMemoryError: PermGen space / Metaspace
- 原因:方法区/元空间内存不足
- 解决方案:增加方法区/元空间大小,检查类加载问题
-
StackOverflowError
- 原因:栈溢出,通常由递归调用过深导致
- 解决方案:检查递归逻辑,增加栈大小
-
OutOfMemoryError: GC overhead limit exceeded
- 原因:垃圾回收效率太低
- 解决方案:增加堆内存,优化代码减少对象创建
5.2 性能调优实例
-
案例一:应用程序启动慢
- 分析:类加载和JIT编译占用时间长
- 解决方案:使用AOT编译,类共享技术
-
案例二:频繁Full GC
- 分析:老年代空间不足或内存泄漏
- 解决方案:增加老年代空间,修复内存泄漏问题
-
案例三:系统吞吐量低
- 分析:GC暂停时间长,影响系统响应
- 解决方案:调整垃圾回收器,使用低延迟收集器如G1或ZGC
六、总结与展望
JVM是Java生态系统的核心组件,理解JVM的工作原理对于编写高效、稳定的Java应用程序至关重要。随着Java技术的不断发展,JVM也在不断优化,如引入了ZGC、Shenandoah等低延迟垃圾收集器,优化了JIT编译器等。
作为Java开发者,我们应当不断学习JVM相关知识,掌握JVM调优技巧,以便在实际工作中解决各种JVM相关问题,提升应用程序性能。
参考资料
- 《深入理解Java虚拟机》(第3版)- 周志明
- Oracle JVM规范
- JVM官方文档
觉得有用的话可以点点赞 (/ω\),支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。