精通Java之JVM原理与实战(一)

本文系统讲解JVM三大核心功能:内存管理、解释执行与即时编译(JIT);深入剖析Java字节码文件结构,包括魔数、主次版本号、常量池、字段、方法及属性;详述类生命周期(加载、连接、初始化、使用、卸载)及双亲委派机制;介绍类加载器体系(Bootstrap/Extension/AppClassLoader)、自定义类加载器与SPI打破委派的实践;涵盖GC算法(标记-清除、复制、标记-整理、分代)及主流回收器(Serial/ParNew/CMS/Parallel/ G1)原理与适用场景。


一、JVM基础

  1. 初识JVM
  • 什么是JVM?JVM全称是Java Virtual Machine,中文译名Java虚拟机。
    图片1
  • JVM功能:
  • 解释和运行: 对字节码文件中的指令,实时的解释成机器码,让计算机执行。
  • 内存管理: 自动为对象、方法等分配内存;自动的垃圾回收机制,回收不再使用的对象。
  • 即时编译: 对热点代码进行优化,提升执行效率。Java语言如果不做任何优化,性能不如C、C++等语言。Java需要实时解释,主要是为了支持跨平台特性。由于JVM需要实时解释虚拟机指令,不做任何优化性能不如直接运行机器码的C、C++等语言。JVM提供了即时编译(Just-In-Time简称JIT)进行性能的优化,最终能达到接近C、C++语言的运行性能,甚至在特定场景下实现超越。
    图片2
  • 常见的JVM:
名称 作者 支持版本 社区活跃度(github star) 特性 适用场景
HotSpot(Oracle JDK版) Oracle 所有版本 高(闭源) 使用最广泛,稳定可靠,社区活跃,JIT支持,Oracle JDK默认虚拟机 默认
HotSpot(Open JDK版) Oracle 所有版本 中(16.1k) 同上,开源,Open JDK默认虚拟机 默认,对JDK有二次开发需求
GraalVM Oracle 11,17,19,企业版支持8 高(18.7k) 多语言支持,高性能、JIT、AOT支持 微服务,云原生架构,需要多语言混合编程
Dragonwell JDK 龙井 Alibaba 标准版8,11,17,扩展版11,17 低(3.9k) 基于OpenJDK的增强,高性能、bug修复、安全性提升,JWarmup、ElasticHeap、Wisp特性支持 电商、物流、金融领域,对性能要求比较高
Eclipse OpenJ9(原IBMJ9) IBM 8,11,17,19,20 低(3.1k) 高性能、可扩展,JIT,AOT特性支持 微服务,云原生架构
  • Java虚拟机规范: <<Java虚拟机规范>>由Oracle制定,内容主要包含了Java虚拟机在设计和实现时需要遵守的规范,主要包含class字节码文件的定义、类和接口的加载和初始化、指令集等内容;<<Java虚拟机规范>>是对虚拟机设计的要求,而不是对Java设计的要求,也就是说虚拟机可以运行在其他的语言比如:Groovy、Scala生成的class字节码文件之上。
  • 总结:
  • JVM到底是什么?JVM全称是Java Virtual Machine,中文译名Java虚拟机,是一个运行在计算机上的程序,它的职责是运行Java字节码文件;
  • JVM的三大核心功能是什么?JVM包含内存管理、解释执行虚拟机指令、即时编译三大功能;
  • 常见的JVM虚拟机有哪些?常见的JVM有HotSpot、GraalVM、OpenJ9等,另外DragonWell龙井JDK也提供了一款功能增强版的JVM。其中使用最广泛的是HotSpot虚拟机。
  1. 字节码文件详解
  • JVM的组成
    图片3
  • 字节码文件的组成
  • 以正确的姿势打开文件: 字节码文件中保存了源代码编译之后的内容,以二进制的方式存储,无法直接用记事本打开阅读;推荐使用jclasslib工具查看字节码文件。
  • 基本信息: 魔数、字节码文件对应的Java版本号、访问标识(public final等)、父类和接口;
名称 作用
Magic魔数 固定位0xCAFEBABE,不会改变
副版本号 编译字节码文件的JDK版本
主版本号 编译字节码文件的JDK版本
访问标识 标识是类还是接口、注解、枚举、模块标识public final abstract
类、父类、接口索引 通过这些索引可以找到类、父类、接口的信息
  • 文件是无法通过文件扩展名来确定文件类型的,文件扩展名可以随意修改,不影响文件的内容。
  • 软件使用文件的头几个字节(文件头)去校验文件的类型,如果软件不支持该种类型就会出错。
  • Java字节码文件中,将文件头称为magic魔数。
文件类型 字节数 文件头
JPEG(jpg) 3 FFD8FF
PNG(png) 4 89504E47(文件尾也有要求)
bmp 2 424D
XML(xml) 5 3C3F786D6C
AVI(avi) 4 41564920
Java字节码文件(.class) 4 CAFEBABE
  • 主副版本号指的是编译字节码文件的JDK版本号,主版本号用来标识大版本号,JDK1.0-1.1使用了45.0-45.3,JDK1.2是46,之后每升级一个大版本就加1;副版本号是当主版本号相同时作为区分不同版本的标识,一般只需要关心主版本号。
  • 常量池: 保存了字符串常量、类或接口名、字段名,主要在字节码指令中使用;
  • 字节码文件中常量池的作用: 避免相同的内容重复定义,节省空间。
  • 常量池中的数据都有一个编号,编号从1开始。在字段或者字节码指令中通过编号可以快速的找到对应的数据。
  • 字节码指令中通过编号引用到常量池的过程称之为符号引用。
  • 字段: 当前类或接口声明的字段信息;
  • 方法: 当前类或接口声明的方法信息、字节码指令;
  • 字节码中的方法区域是存放字节码指令的核心位置,字节码指令的内容存放在方法的Code属性中。
  • 操作数栈是临时存放数据的地方,局部变量表是存放方法中的局部变量的位置。
    图片4
    图片5
  • int i = 0; i = i++;最终i的值是多少?0,通过分析字节码指令发现,i++先把0取出来放入临时的操作数栈中,接下来对i进行加1,i变成了1,最后再将之前保存的临时值0放入局部变量表数组1位置,将1覆盖为0,最后i就变成了0。
    图片6
  • 属性: 类的属性,比如: 源码的文件名、内部类的列表等。
  • 字节码常用工具:
  • javap -v命令
  • javap是JDK自带的反编译工具,可以通过控制台查看字节码文件的内容。适合在服务器上查看字节码文件内容。
  • 直接输入javap查看所有参数。
  • 输入javap -v 字节码文件名称 查看具体的字节码信息。(如果jar包需要先使用jar -xvf 命令解压)
  • jclasslib插件
  • jclasslib也有idea插件版本,建议开发时使用idea插件版本,可以在代码编译之后实时看到字节码文件内容。
  • 阿里arthas
  • Arthas是一款线上监控诊断产品,通过全局视角实时查看应用load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,大大提升线上问题排查效率。
    图片7
  • dump 类的全限定名: dump已加载类的字节码文件到特定目录。
  • jad 类的全限定名: 反编译已加载类的源码。
  • 总结:
  • 如何查看字节码文件?本地文件可以使用jclasslib工具查看,开发环境使用jclasslib插件;服务器上文件使用javap命令直接查看,也可以通过arthas的dump命令导出字节码文件再查看本地文件;还可以使用jad命令反编译出源代码。
  • 字节码文件的核心组成有哪些?基本信息: 魔数、字节码文件对应的Java版本号、访问标识(public final等),父类和接口;常量池: 保存了字符串常量、类或接口名、字段名,主要在字节码指令中使用;字段:当前类或接口声明的字段信息;方法: 当前类或接口声明的方法信息,字节码指令;属性: 类的属性,比如: 源码的文件名,内部类的列表等。
  1. 类的生命周期
  • 类的生命周期描述了一个类加载、使用、卸载的整个过程。
  • 加载(Loading)–>连接(Linking)–>初始化(Initialization)–>使用(Using)–>卸载(Unloading)。
  • 连接(Linking): 验证–>准备–>解析。
  • 加载阶段:
  • 加载(Loading)阶段第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息。程序员可以使用Java代码拓展的不同的渠道。
  • 类加载器在加载完类之后,Java虚拟机会将字节码中的信息保存到方法区中。
  • 类加载器在加载完类之后,Java虚拟机会将字节码中的信息保存到内存的方法区中。生成一个InstanceKlass对象,保存类的所有信息,里边还包含实现特定功能比如:多态的信息。
    图片8
  • 同时,Java虚拟机还会在堆中生成一份与方法区中数据类似的java.lang.Class对象。作用是在java代码中去获取类的信息以及存储静态字段的数据(JDK8及以后)。
    图片9
  • 对于开发者来说,只需要访问堆中的Class对象而不需要访问方法区中所有信息。这样Java虚拟机就能很好的控制开发者访问数据的范围。
    图片10
  • 查看内存中的对象
  • 推荐使用JDK自带的hsdb工具查看Java虚拟机内存信息。工具位于JDK安装目录下lib文件夹中的sa-jdi.jar中(JDK8中)。
  • 启动命令: java -cp sa-jdi.jar sum.jvm.hotspot.HSDB
  • 连接阶段:
  • 验证
  • 连接(Linking)阶段的第一个环节是验证,验证的主要目的是检测Java字节码文件是否遵守了<<Java虚拟机规范>>中的约束。这个阶段一般不需要程序员参与。
  • 主要包含如下四部分,具体详见<<Java虚拟机规范>>:
  • 文件格式验证,比如:文件是否以0xCAFEBABE开头,主次版本号是否满足当前Java虚拟机版本要求。
    图片11
  • 元信息验证,例如:类必须有父类(super不能为空)。
    图片12
  • 验证程序执行指令的语义,比如:方法内的指令执行到一半强行跳转到其他方法中去。
    图片13
  • 符号引用验证,例如: 是否访问了其他类中private的方法等。
  • 准备
  • 准备阶段为静态变量(static)分配内存并设置初始值。
    图片14
  • 准备阶段只会给静态变量赋初始值.而每一种基本数据类型和引用数据类型都有其初始值。
数据类型 初始值
int 0
long 0L
short 0
char ‘\u0000’
byte 0
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yumiwangzi30

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值