在现代软件开发中,Java 语言凭借其“一次编写,到处运行”的理念成为了企业级应用的首选之一。这一理念的背后支撑技术正是 Java 虚拟机(JVM)。JVM 是一个抽象的计算机,它实现了 Java 编程语言的各种特性,并且能够执行编译后的字节码文件。了解 JVM 的工作原理对于优化程序性能、调试代码以及深入理解 Java 应用至关重要。
本文将简要介绍 JVM 中常见的指令及其作用,旨在为开发者提供一个清晰的认识框架,帮助他们更好地掌握 JVM 指令集的基本概念及其实战技巧。我们将从最基础的加载和存储指令开始,逐步探讨控制转移、方法调用等更复杂的操作符,最终带领读者深入了解 JVM 内部工作机制的一角。
详解JVM常见指令
jinfo(查看配置信息)
查看Java应用程序配置参数或者JVM系统属性,相关命令详情我们可以使用-help或者man命令查看:
对此我们也给出jinfo指令集的说明:
为了演示,笔者在服务器上开启了一个Java应用,我们可以使用jps命令查看其进程id,可以看到笔者服务器中有一个pid为19946的Java进程,查看当前应用所有的配置参数以及系统配置属性命令为jinfo pid,可以看到通过jinfo结合进程号即可看到这个java进程对应的各自类库、版本号以及系统信息:
如果我们希望查看当前Java应用是否有配置某些信息,可以使用命令jinfo -flag 配置选项 pid,例如我们想查看当前应用是否有开启gc选项,可以使用下面这段命令:
可以看到输出结果为-XX:-PrintGC,因为PrintGC前面是减号,这说明该选项并没有开启。
如果我们希望将这个选项开启,我们只需在参数前面加个+号即可,例如我们希望开启gc选项,我们只需键入如下命令:
再次查看可以发现,选项生效了:
有些参数是键值对的形式,例如我们想配置dump日志的路径,我们也可以使用jinfo进行配置,命令格式为jinfo -flag 参数=值 Java进程id:
打印JVM选项信息可用指令jinfo -flags pid:
查看应用属性,命令格式jinfo -sysprops Java进程id:
jmap(查看堆区信息、对象信息等)
在没有arthas之前,我们打印内存快照大部分都是通过jmap,大体来说jmap支持如下几个功能:
- 查看使用的GC算法,堆的配置信息以及各个内存区域的内存使用情况
- 显示堆对象的统计信息,包括每一个Java类、对象数量、内存大小、类名称等
- 打印等会回收的对象的信息
- 生成dump文件,配合jhat或者mat使用
查看堆内存使用情况我们可以使用jmap -heap Java进程id,例如:
对应的我们就可以看到当前java进程堆内存的使用情况:
查看存活的Java对象(文档说明:to print histogram of java object heap; if the "live" suboption is specified, only count live objects),命令格式:jmap -histo:live Java进程id,所以我们的指令即:
输出结果如下,我们可以很直观的看到各个实例对应占用的内存空间大小:
打印正在被回收的类**(文档说明:to print information on objects awaiting finalization)**,命令格式:jmap -finalizerinfo Java进程id,所以我们的指令就是:
对此我们既可以看到正在被打印的对象数量等信息:
dump也就是我们生成内存快照的指令,比较使用,如下所示,这就是将存活的对象的信息存到二进制文件heap.bin中:
此时就可以使用jhat打开该文件:
如下所示jhat 文件名,这时候我们就可以通过7000端口查看详情了:
jstat(常用,监控运行时状态信息)
jstat用于监控虚拟机各种运行状态信息,显示虚拟机进程中装载、内存、垃圾收集、JIT编译等运行数据。
查看类加载信息,命令格式jstat -class Java进程id,以我们的进程为例,对应的指令就是:
输出结果如下,我们可以看到如下输出结果:
对应的含义为:
Loaded: 当前已加载的类的数量。 Bytes: 已加载类所占用的字节数。 Unloaded: 卸载的类的数量。 Bytes: 卸载的类所释放的字节数。 Time: 加载和卸载类所花费的时间(以秒为单位)。
查看编译统计信息jstat -compiler Java进程id,对应我们进程的使用指令就是:
对应的输出结果如下,其中这各个参数的含义分别是:
Compiled: 已编译的方法数量。 Failed: 编译失败的方法数量。 Invalid: 因为各种原因被标记为无效的方法数量。 Time: 编译所花费的时间(以毫秒为单位)。 FailedType: 最后一次编译失败的原因类型。 FailedMethod: 最后一次编译失败的方法名称及其签名。
查看gc统计信息,该信息我们只需通过jstat的gc选项即可查看了:
对应输出结果如下每个表头的含义如下
这里我们也给出上文中各个字段的含义:
- S0C :年轻代中第一个survivor(幸存区)的容量 (字节)
- S1C :年轻代中第二个survivor(幸存区)的容量 (字节)
- S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
- S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节)
- EC:年轻代中Eden(伊甸园)的容量 (字节)
- EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)
- OC:Old代的容量 (字节)
- OU:Old代目前已使用空间 (字节)
- PC:Perm(持久代)的容量 (字节)
- PU:Perm(持久代)目前已使用空间 (字节)
- YGC:从应用程序启动到采样时年轻代中gc次数
- YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)
- FGC:从应用程序启动到采样时old代(全gc)gc次数
- FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)
- GCT:从应用程序启动到采样时gc用的总时间(s)
查看gc内存容量和元空间容量:
对应的输出结果如下,这样我们就可以很直观查看到如下几个指标:
- NGCMN: 新生代最小容量(New Generation Minimum Capacity)。
- NGCMX: 新生代最大容量(New Generation Maximum Capacity)。
- NGC: 当前新生代容量(Current New Generation Capacity)。
- S0C: 生存空间 0 容量(Survivor Space 0 Capacity)。
- S1C: 生存空间 1 容量(Survivor Space 1 Capacity)。
- EC: Eden 区域容量(Eden Space Capacity)。
- OGCMN: 老年代最小容量(Old Generation Minimum Capacity)。
- OGCMX: 老年代最大容量(Old Generation Maximum Capacity)。
- OGC: 当前老年代容量(Current Old Generation Capacity)。
- MCMN: 元空间最小容量(Metaspace Minimum Capacity)。
- MCMX: 元空间最大容量(Metaspace Maximum Capacity)。
- MC: 当前元空间容量(Current Metaspace Capacity)。
对应的输出结果如下:
查看年轻代统计信息:
对应表头各个参数含义为:
- S0C: 生存空间 0 容量(Survivor Space 0 Capacity)。
- S1C: 生存空间 1 容量(Survivor Space 1 Capacity)。
- S0U: 生存空间 0 已使用容量(Survivor Space 0 Used)。
- S1U: 生存空间 1 已使用容量(Survivor Space 1 Used)。
- TT: 晋升阈值(Tenuring Threshold),表示对象在新生代中存活多少次 Minor GC 后会被晋升到老年代。
- MTT: 最大晋升阈值(Maximum Tenuring Threshold)。
- DSS: 欲望的生存空间大小(Desired Survivor Size),即期望的 Survivor 空间大小。
- EC: Eden 区域容量(Eden Space Capacity)。
- EU: Eden 区域已使用容量(Eden Space Used)。