JVM 指令集概览:基础与应用

开发
本文将简要介绍 JVM 中常见的指令及其作用,旨在为开发者提供一个清晰的认识框架,帮助他们更好地掌握 JVM 指令集的基本概念及其实战技巧。

在现代软件开发中,Java 语言凭借其“一次编写,到处运行”的理念成为了企业级应用的首选之一。这一理念的背后支撑技术正是 Java 虚拟机(JVM)。JVM 是一个抽象的计算机,它实现了 Java 编程语言的各种特性,并且能够执行编译后的字节码文件。了解 JVM 的工作原理对于优化程序性能、调试代码以及深入理解 Java 应用至关重要。

本文将简要介绍 JVM 中常见的指令及其作用,旨在为开发者提供一个清晰的认识框架,帮助他们更好地掌握 JVM 指令集的基本概念及其实战技巧。我们将从最基础的加载和存储指令开始,逐步探讨控制转移、方法调用等更复杂的操作符,最终带领读者深入了解 JVM 内部工作机制的一角。

详解JVM常见指令

jinfo(查看配置信息)

查看Java应用程序配置参数或者JVM系统属性,相关命令详情我们可以使用-help或者man命令查看:

jinfo -help

对此我们也给出jinfo指令集的说明:

Usage:
    jinfo [option] <pid>
        (to connect to running process)
    jinfo [option] <executable <core>
        (to connect to a core file)
    jinfo [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    -flag <name>         to print the value of the named VM flag
    -flag [+|-]<name>    to enable or disable the named VM flag
    -flag <name>=<value> to set the named VM flag to the given value
    -flags               to print VM flags
    -sysprops            to print Java system properties
    <no option>          to print both of the above
    -h | -help           to print this help message

为了演示,笔者在服务器上开启了一个Java应用,我们可以使用jps命令查看其进程id,可以看到笔者服务器中有一个pid为19946的Java进程,查看当前应用所有的配置参数以及系统配置属性命令为jinfo pid,可以看到通过jinfo结合进程号即可看到这个java进程对应的各自类库、版本号以及系统信息:

Attaching to process ID 19946, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.202-b08 Java System Properties: java.runtime.name = Java(TM) SE Runtime Environment java.vm.version = 25.202-b08 sun.boot.library.path = /root/jdk8/jre/lib/amd64 java.protocol.handler.pkgs = org.springframework.boot.loader java.vendor.url = http://java.oracle.com/ java.vm.vendor = Oracle Corporation path.separator = : file.encoding.pkg = sun.io java.vm.name = Java HotSpot(TM) 64-Bit Server VM sun.os.patch.level = unknown sun.java.launcher = SUN_STANDARD user.country = US user.dir = /tmp ......

如果我们希望查看当前Java应用是否有配置某些信息,可以使用命令jinfo -flag 配置选项 pid,例如我们想查看当前应用是否有开启gc选项,可以使用下面这段命令:

jinfo -flag PrintGC 19946

可以看到输出结果为-XX:-PrintGC,因为PrintGC前面是减号,这说明该选项并没有开启。

-XX:-PrintGC

如果我们希望将这个选项开启,我们只需在参数前面加个+号即可,例如我们希望开启gc选项,我们只需键入如下命令:

 jinfo -flag +PrintGC 19946

再次查看可以发现,选项生效了:

[root@xxxx tmp]# jinfo -flag PrintGC 19946
-XX:+PrintGC

有些参数是键值对的形式,例如我们想配置dump日志的路径,我们也可以使用jinfo进行配置,命令格式为jinfo -flag 参数=值 Java进程id:

jinfo -flag HeapDumpPath=/tmp/dump.log 19946

打印JVM选项信息可用指令jinfo -flags pid:

Attaching to process ID 4854, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.202-b08
Non-default VM flags: -XX:CICompilerCount=2 -XX:HeapDumpPath=null -XX:InitialHeapSize=33554432 -XX:MaxHeapSize=511705088 -XX:MaxNewSize=170524672 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=11141120 -XX:OldSize=22413312 -XX:+PrintGC -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps
Command line:

查看应用属性,命令格式jinfo -sysprops Java进程id:

jinfo -sysprops 2341jinfo -sysprops 2341

jmap(查看堆区信息、对象信息等)

在没有arthas之前,我们打印内存快照大部分都是通过jmap,大体来说jmap支持如下几个功能:

  • 查看使用的GC算法,堆的配置信息以及各个内存区域的内存使用情况
  • 显示堆对象的统计信息,包括每一个Java类、对象数量、内存大小、类名称等
  • 打印等会回收的对象的信息
  • 生成dump文件,配合jhat或者mat使用

查看堆内存使用情况我们可以使用jmap -heap Java进程id,例如:

 jmap -heap 25534

对应的我们就可以看到当前java进程堆内存的使用情况:

Attaching to process ID 25534, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.202-b08

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 511705088 (488.0MB)
   NewSize                  = 11141120 (10.625MB)
   MaxNewSize               = 170524672 (162.625MB)
   OldSize                  = 22413312 (21.375MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

查看存活的Java对象(文档说明:to print histogram of java object heap; if the "live" suboption is specified, only count live objects),命令格式:jmap -histo:live Java进程id,所以我们的指令即:

jmap -histo:live 25534

输出结果如下,我们可以很直观的看到各个实例对应占用的内存空间大小:

 num     #instances         #bytes  class name
----------------------------------------------
   1:         48676        7974952  [C
   2:          7762        1873312  [I
   3:         47785        1146840  java.lang.String
   4:         12737        1120856  java.lang.reflect.Method
   5:          8773         968912  java.lang.Class
   6:         25572         818304  java.util.concurrent.ConcurrentHashMap$Node
   7:         14108         564320  java.util.LinkedHashMap$Entry
   8:          2712         509536  [B
   9:          9308         494936  [Ljava.lang.Object;
  10:          6345         493128  [Ljava.util.HashMap$Node;
  11:          7001         392056  java.util.LinkedHashMap
  12:         11255         360160  java.util.HashMap$Node
  13:         15946         354528  [Ljava.lang.Class;
  14:         18176         290816  java.lang.Object
  15:          3447         248184  java.lang.reflect.Field
  16:           124         192320  [Ljava.util.concurrent.ConcurrentHashMap$Node;

打印正在被回收的类**(文档说明:to print information on objects awaiting finalization)**,命令格式:jmap -finalizerinfo Java进程id,所以我们的指令就是:

jmap -finalizerinfo 25534

对此我们既可以看到正在被打印的对象数量等信息:

Attaching to process ID 25534, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.202-b08
Number of objects pending for finalization: 0

dump也就是我们生成内存快照的指令,比较使用,如下所示,这就是将存活的对象的信息存到二进制文件heap.bin中:

 jmap -dump:live,format=b,file=/tmp/heap.bin 25534

此时就可以使用jhat打开该文件:

 jhat heap.bin

如下所示jhat 文件名,这时候我们就可以通过7000端口查看详情了:

Reading from heap.bin... Dump file created Wed Nov 02 20:21:18 CST 2022 Snapshot read, resolving... Resolving 346825 objects... Chasing references, expect 69 dots..................................................................... Eliminating duplicate references..................................................................... Snapshot resolved. Started HTTP server on port 7000 Server is ready.

jstat(常用,监控运行时状态信息)

jstat用于监控虚拟机各种运行状态信息,显示虚拟机进程中装载、内存、垃圾收集、JIT编译等运行数据。

查看类加载信息,命令格式jstat -class Java进程id,以我们的进程为例,对应的指令就是:

jstat -class 2341

输出结果如下,我们可以看到如下输出结果:

Loaded  Bytes  Unloaded  Bytes     Time
  8221 14604.3        1     0.9      12.74

对应的含义为:

Loaded: 当前已加载的类的数量。 Bytes: 已加载类所占用的字节数。 Unloaded: 卸载的类的数量。 Bytes: 卸载的类所释放的字节数。 Time: 加载和卸载类所花费的时间(以秒为单位)。

查看编译统计信息jstat -compiler Java进程id,对应我们进程的使用指令就是:

jstat -compiler 2341

对应的输出结果如下,其中这各个参数的含义分别是:

Compiled: 已编译的方法数量。 Failed: 编译失败的方法数量。 Invalid: 因为各种原因被标记为无效的方法数量。 Time: 编译所花费的时间(以毫秒为单位)。 FailedType: 最后一次编译失败的原因类型。 FailedMethod: 最后一次编译失败的方法名称及其签名。

Compiled Failed Invalid   Time   FailedType FailedMethod
    4177      0       0    17.68          0

查看gc统计信息,该信息我们只需通过jstat的gc选项即可查看了:

jstat -gc 2341

对应输出结果如下每个表头的含义如下

S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 1408.0 1408.0 0.0 1020.3 11840.0 7493.1 29268.0 22168.5 42840.0 40613.5 5760.0 5311.9 65 0.401 2 0.213 0.613

这里我们也给出上文中各个字段的含义:

  • 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内存容量和元空间容量:

 jstat -gccapacity 2341

对应的输出结果如下,这样我们就可以很直观查看到如下几个指标:

  • 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)。

对应的输出结果如下:

 NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC         OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC
 10880.0 166528.0  14656.0 1408.0 1408.0  11840.0    21888.0   333184.0    29268.0    29268.0      0.0 1087488.0  42840.0      0.0 1048576.0   5760.0     65     2

查看年轻代统计信息:

jstat -gcnew 2341

对应表头各个参数含义为:

  • 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)。
 S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT
1408.0 1408.0    0.0 1020.3  2  15  704.0  11840.0   7652.5     65    0.401
责任编辑:赵宁宁 来源: 写代码的SharkChili
相关推荐

2021-05-27 07:54:21

CPU 指令集CISC

2009-03-28 17:10:34

NehalemIntel服务器

2023-09-14 07:25:07

自主指令集核心

2021-06-07 08:15:13

CPU指令集程序员

2022-07-05 08:34:22

虚拟机JavaJVM

2021-01-30 12:39:16

Linux特权模式

2010-09-27 08:38:49

JVM堆JVM栈

2019-05-22 08:43:45

指令集RISC-V开源

2020-10-19 06:52:19

CortexARMv8arm

2023-07-27 06:50:38

Inte升级版AVX10。

2023-07-26 06:52:37

AVX10AVX10.1指令

2019-10-10 09:52:42

芯片半导体技术

2018-10-19 15:01:48

AMD ZenFMA4处理器

2021-10-26 17:26:46

JVM架构模型

2010-09-17 15:12:57

JVMJava虚拟机

2024-02-26 10:21:28

Windows 11微软操作系统吧

2012-01-11 13:04:40

JavaJVM

2023-07-10 16:00:56

AT指令建立网络连接

2009-08-10 18:46:07

ASP.NET基础语法

2023-09-01 08:59:57

点赞
收藏

51CTO技术栈公众号