引言
JPS命令是日常开发过程中经常遇到的命令。使用起来也非常简单,本节内容主要翻译Oracel官方的JPS说明,以及相关的实现原理分析,最后介绍一些JPS无法获取到JAVA进程的原因排查。
官方文档翻译
原文:jps - Java Virtual Machine Process Status Tool (oracle.com)
SYNOPSIS(简介)
jps [ options] [ hostid ]
PARAMETERS
Command-line options.
The host identifier of the host for which the process report should be generated. The _hostid_ may include optional components that indicate the communications protocol, port number, and other implementation specific data.
host唯一主机标识符号,这里实际指的是操作系统管理进程必须为应用程序分配的进程号,然后再由JVM统一管理资源标识。统一资源管理可以是协议,端口号和其他特殊数据。
OPTIONS
The jps command supports a number of options that modify the output of the command. These options are subject to change or removal in the future.
jps 命令支持许多修改命令输出的选项。这些选项将来可能会更改或删除。
-q:Suppress the output of the class name, JAR file name, and arguments passed to the main
method, producing only a list of local VM identifiers.
禁止输出类名、JAR 文档名和传递给“main”方法的参数,仅生成本地 VM 标识符的列表。
[zxd@localhost ~]$ jps
10635 Jps
[zxd@localhost ~]$ jps -q
10647
-m:Output the arguments passed to the main method. The output may be null for embedded JVMs.
输出传递给 main 方法的参数。对于嵌入式 JVM,输出可能为空。
[root@localhost bin]# jps -m
11057 seata-server.jar
10726 nacos-server.jar --spring.config.additional-location=file:/opt/nacos/conf/ --logging.config=/opt/nacos/conf/nacos-logback.xml --server.max-http-header-size=524288 nacos.nacos
11103 Jps -m
-l:Output the full package name for the application's main class or the full path name to the application's JAR file.
输出应用程序的启动main包的完整名称或完整路径。
[root@localhost bin]# jps -l
10726 /opt/nacos/target/nacos-server.jar
15831 sun.tools.jps.Jps
15742 /opt/seata/target/seata-server.jar
-v:Output the arguments passed to the JVM.
注意小写,输出传递给 JVM 的参数。
[root@localhost bin]# jps -v
15843 Jps -Dapplication.home=/opt/jdk8 -Xms8m
10726 nacos-server.jar -Djava.ext.dirs=/opt/jdk8/jre/lib/ext:/opt/jdk8/lib/ext -Xms512m -Xmx512m -Xmn256m -Dnacos.standalone=true -Dnacos.member.list= -Xloggc:/opt/nacos/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Dloader.path=/opt/nacos/plugins,/opt/nacos/plugins/health,/opt/nacos/plugins/cmdb,/opt/nacos/plugins/selector -Dnacos.home=/opt/nacos
15742 seata-server.jar -Dloader.path=/opt/seata/lib -Xmx512m -Xms512m -Xmn256m -Xss512k -XX:SurvivorRatio=10 -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:MaxDirectMemorySize=1024m -XX:-OmitStackTraceInFastThrow -XX:-UseAdaptiveSizePolicy -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/seata/logs/java_heapdump.hprof -XX:+DisableExplicitGC -Xloggc:/opt/seata/logs/seata_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -XX:+UseG1GC -Dio.netty.leakDetectionLevel=advanced -Dapp.name=seata-server -Dapp.pid=15703 -Dapp.home=/opt/seata -Dbasedir=/opt/seata -Dspring.config.location=/opt/seata/conf/application.yml -Dlogging.config=/opt/seata/conf/logback-spring.xml
-V:Output the arguments passed to the JVM through the flags file (the .hotspotrc file or the file specified by the -XX:Flags=<_filename_> argument).
注意大写,输出通过标志文档(.hotspotrc 文档或由 -XX:Flags=<_filename_> 参数指定的文档)传递给 JVM 的参数。
这个参数的效果从目前看来和默认的使用方式类似,并未看到实质的作用。
-J :_option_
Pass _option_ to the java launcher called by jps. For example, -J-Xms48m sets the startup memory to 48 megabytes. It is a common convention for -J to pass options to the underlying VM executing applications written in Java.
将 option 传递给 jps 调用的 java 启动器。例如,-J-Xms48m 将启动内存设置为 48 MB。-J 将选项传递给执行用 Java 编写的应用进程的底层 VM 是一种常见的约定。
HOST IDENTIFIER
接下来时第二个参数,如果不指定默认为本机的JVM程序,否则看以指定对应的主机
The host identifier, or _hostid_ is a string that indicates the target system. The syntax of the _hostid_ string largely corresponds to the syntax of a URI:
host标识或者hostid以字符串形式显示在目标机器,\_hostid\_字符串的语法与URI的语法基本对应。
[protocol:][//]hostname] [:port][ /servername ]
protocol
The communications protocol. If the _protocol_ is omitted and a _hostname_ is not specified, the default protocol is a platform specific, optimized, local protocol. If the _protocol_ is omitted and a _hostname_ is specified, then the default protocol is rmi.
通信协议。如果省略 protocol 且未指定 hostname_,则默认协议是特定于平台的本地协议。如果省略 _protocol 并指定了 _hostname_,则默认协议为 rmi。
hostname
A hostname or IP address indicating the target host. If _hostname_ is omitted, then the target host is the local host.
指示目标主机的主机名或 IP 地址。如果省略 _hostname_,则目标主机是本地主机。
port
The default port for communicating with the remote server. If the _hostname_ is omitted or the _protocol_ specifies an optimized, local protocol, then _port_ is ignored. Otherwise, treatment of the _port_ parameter is implementation specific. For the default rmi protocol the _port_ indicates the port number for the rmiregistry on the remote host. If _port_ is omitted, and _protocol_ indicates rmi, then the default rmiregistry port (1099) is used.
用于与远程服务器通信的默认端口。如果省略 hostname 或 protocol 指定了优化的本地协议,则会忽略 port_。否则,_port 参数的处理是特定于实现的。对于缺省的 rmi 协议,_port_ 指示远程主机上 rmiregistry 的端口号。如果省略 port_,并且 _protocol 表示 rmi,则使用默认的 rmiregistry 端口 (1099)。
servername
The treatment of this parameter depends on the implementation. For the optimized, local protocol, this field is ignored. For the rmi protocol, this parameter is a string representing the name of the RMI remote object on the remote host. See the -n option for the jstatd command.
此参数的处理取决于实现。对于优化的本地协议,将忽略此字段。对于 rmi 协议,此参数是一个字符串,表示远程主机上 RMI 远程对象的名称。请参阅 jstatd 命令的 -n 选项。
DESCRIPTION(描述)
The jps tool lists the instrumented HotSpot Java Virtual Machines (JVMs) on the target system. The tool is limited to reporting information on JVMs for which it has the access permissions.
JPS 的作用是列举目标JVM当前运行的所有程序,但是仅限于当前JVM接管的所有程序。
If jps is run without specifying a _hostid_, it will look for instrumented JVMs on the local host. If started with a _hostid_, it will look for JVMs on the indicated host, using the specified protocol and port.
JPS 运行的时候不是一个特别的hostid,而是会在本地运行并且检测JVM,并且看上去像是在JVM的进程李米娜,使用特殊的协议和端口。
A jstatd process is assumed to be running on the target host. The jps command will report the local VM identifier, or _lvmid_, for each instrumented JVM found on the target system. The lvmid is typically, but not necessarily, the operating system's process identifier for the JVM process. With no options, jps will list each Java application's _lvmid_ followed by the short form of the application's class name or jar file name. The short form of the class name or JAR file name omits the class's package information or the JAR files path information.
假设目标主机上有一个jstatd进程正在运行,JPS命令会报告当前的VM标识或者lvmid,对于在目标系统上发现的每个工具化的JVM来说,lvmid 是普遍但是必要的,由操作系统的进程标识
The jps command uses the java launcher to find the class name and arguments passed to the main
method. If the target JVM is started with a custom launcher, the class name (or JAR file name) and the arguments to the main
method will not be available. In this case, the jps command will output the string _Unknown_ for the class name or JAR file name and for the arguments to the main method.
jps 命令使用 java 启动器来查找传递给 'main' 方法的类名和参数。如果目标 JVM 是使用定制启动器启动的,那幺类名(或 JAR 文档名)和 'main' 方法的参数将不可用。在这种情况下,jps 命令将输出字符串 Unknown 作为类名或 JAR 文档名以及 main 方法的参数。
The list of JVMs produced by the jps command may be limited by the permissions granted to the principal running the command. The command will only list the JVMs for which the principle has access rights as determined by operating system specific access control mechanisms.
由 jps 命令生成的 JVM 列表可能受到授予运行该命令的主体的权限的限制。命令将仅列出主体具有访问权限的 JVM,该访问权限由特定于操作系统的访问控制机制确定。
NOTE: This utility is unsupported and may not be available in future versions of the JDK. It is not currently available on Windows 98 and Windows ME platforms.
此实用进程在未来JDK版本中可能会不受支持,并且在部分老旧操作系统是不支持的。
OUTPUT FORMAT
The output of the jps command follows the following pattern:
lvmid [ [ classname | JARfilename | "Unknown"] [ _arg_ ] [ jvmarg ] ]
Where all output tokens are separated by white space. An _arg_ that includes embedded white space will introduce ambiguity when attempting to map arguments to their actual positional parameters.
其中所有输出标记都用空格分隔。包含嵌入空格的 arg 在尝试将参数映射到其实际位置参数时会引入歧义。
NOTE: You are advised not to write scripts to parse jps output since the format may change in future releases. If you choose to write scripts that parse jps output, expect to modify them for future releases of this tool.
注意:建议您不要编写脚本来解析 jps 输出,因为格式可能会在将来的版本中发生变化。如果选择编写分析 jps 输出的脚本,请为此工具的未来版本修改它们。
EXAMPLES
This section provides examples of the jps command.
下面是JPS命令使用的一些案例:
Listing the instrumented JVMs on the local host:
列出当前机器允许的JVM:
jps
18027 Java2Demo.JAR
18032 jps
18005 jstat
Listing the instrumented JVMs on a remote host:
This example assumes that the jstat server and either the its internal RMI registry or a separate external rmiregistry process are running on the remote host on the default port (port 1099). It also assumes that the local host has appropriate permissions to access the remote host. This example also includes the _-l_ option to output the long form of the class names or JAR file names.
此示例假定 jstat 服务器及其内部 RMI 注册表或单独的外部 rmiregistry 进程在远程机上的缺省端口(端口 1099)上运行。它还假定本地主机具有访问远程主机的适当权限。此示例还包括 -l 选项,用于输出类名或 JAR 文档名的长格式。
jps -l remote.domain
3002 /opt/jdk1.7.0/demo/jfc/Java2D/Java2Demo.JAR
2857 sun.tools.jstatd.jstatd
Listing the instrumented JVMs on a remote host with a non-default port for the RMI registry
This example assumes that the jstatd server, with an internal RMI registry bound to port 2002, is running on the remote host. This example also uses the _-m_ option to include the arguments passed to the _main_ method of each of the listed Java applications.
下面是一个打印远程服务器的JVM运行情况。
此示例假定远程主机上正在运行具有绑定到端口 2002 的内部 RMI 注册表的 jstatd 服务器。此示例还使用 -m 选项来包含传递给列出的每个 Java 应用进程的 main 方法的参数
jps -m remote.domain:2002
3002 /opt/jdk1.7.0/demo/jfc/Java2D/Java2Demo.JAR
3102 sun.tools.jstatd.jstatd -p 2002
SEE ALSO
- java - the Java Application Launcher
- jstat - the Java virtual machine Statistics Monitoring Tool
- jstatd - the jstat daemon
- rmiregistry - the Java Remote Object Registry
实现原理
jps的命令是在$JAVA_HOME/bin
下面存在的,显然和每个JVM启动都有密切关系的,既然 JPS 要搜集所有JVM的信息:
[root@localhost bin]# ls /opt/jdk8/bin/
appletviewer jarsigner javah jcmd jhat jrunscript jvisualvm policytool serialver wsimport
ControlPanel java javap jconsole jinfo jsadebugd keytool rmic servertool xjc
extcheck javac javapackager jcontrol jjs jstack native2ascii rmid tnameserv
idlj javadoc java-rmi.cgi jdb jmap jstat orbd rmiregistry unpack200
jar javafxpackager javaws jdeps jps jstatd pack200 schemagen wsgen
java程序在启动以后,会在java.io.tmpdir
指定的目录下,就是临时文件夹里,在Linux中会生成一个类似于hsperfdata_User(User为登录用户)的文件夹,这个文件夹里(在Linux中为/tmp/hsperfdata_{userName}/
)有几个文件,名字就是java进程的pid,因此列出当前运行的java进程,只是把这个目录里的文件名列一下而已。 至于系统的参数什么,就可以解析这几个文件获得。
我们可以实际运行实验看一下:
[root@localhost tmp]# cd /tmp/hsperfdata_root/
[root@localhost hsperfdata_root]# ls
10726 15742
[root@localhost hsperfdata_root]# jps
10726 nacos-server.jar
15742 seata-server.jar
16270 Jps
我们通过进程号可以看到是可以对应上的。java.nio.file.TempFileHelper
中保存着临时文件的构建逻辑,注意这里对文件夹做了一层安全加密,防止外部应用篡改。
// temporary directory location
private static final Path tmpdir =
Paths.get(doPrivileged(new GetPropertyAction("java.io.tmpdir")));
JPS失效处理
因为JPS需要有对应的JVM需要具备访问权限才可以查看,对于内嵌的JVM是无法利用JPS查看。所以有时候会出现用ps -ef|grep java
能看到启动的java进程,但是用jps查看却不存在该进程的id。
对于这样的JPS失效场景,我们可以通过下面的一些要点排查:
- 磁盘读写、目录权限问题 若该用户没有权限写/tmp目录或是磁盘已满,则无法创建/tmp/hsperfdata_userName/pid文件。
临时文件丢失,被删除或是定期清理。比较常见的文件丢失原因可能是定时删除临时目录的工具,比如
crontab
、redhat
的tmpwatch
、ubuntu
的tmpreaper
等等用jconsole监控进程,发现在某一时段后进程仍然存在,但是却没有监控信息了。
- JAVA的进程信息存储被转移,JPS是无法搜集到这一类信息的,比如内嵌的JVM,此外java启动时提供了参数(-Djava.io.tmpdir)可以轻松修改进程信息启动和存储目录。但是这个设置的改变会导致JPS和Jconsole无法读取到。
杀进程
这里介绍比较常见但是实际上 完全不推荐使用的方法:
[root@localhost hsperfdata_root]# jps
10726 nacos-server.jar
16779 Jps
15742 seata-server.jar
[root@localhost hsperfdata_root]# kill -9 15742
当然 ps -ef | grep java
或者使用ps -aux | grep java
查找进程号,然后kill -9 进程号
,这个进程号在搜索结果里面处于第二列,比如这里如果我想要kill掉dashboard,就可以使用kill -9 4002
结束进程:
ps -ef | grep java 的内容:
root 4002 3942 0 14:56 pts/0 00:00:00 sh -c java $JAVA_OPTS -jar /rocketmq-dashboard.jar
root 4628 4002 0 14:56 pts/0 00:01:58 java -Drocketmq.namesrv.addr=192.168.58.128:9876 -jar /rocketmq-dashboard.jar
3000 4997 4916 0 14:56 ? 00:01:08 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.332.b09-1.el7_9.x86_64/bin/java -server -Xms909M -Xmx909M -Xmn200M -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:-UseParNewGC -verbose:gc -Xloggc:/dev/shm/rmq_srv_gc.log -XX:+PrintGCDetails -XX:-OmitStackTraceInFastThrow -XX:-UseLargePages -Djava.ext.dirs=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.332.b09-1.el7_9.x86_64/jre/lib/ext:/home/rocketmq/rocketmq-4.9.4/bin/../lib -cp .:/home/rocketmq/rocketmq-4.9.4/bin/../conf: org.apache.rocketmq.namesrv.NamesrvStartup
root 10726 1 0 15:44 pts/0 00:04:14 /opt/jdk8/bin/java -Djava.ext.dirs=/opt/jdk8/jre/lib/ext:/opt/jdk8/lib/ext -Xms512m -Xmx512m -Xmn256m -Dnacos.standalone=true -Dnacos.member.list= -Xloggc:/opt/nacos/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Dloader.path=/opt/nacos/plugins,/opt/nacos/plugins/health,/opt/nacos/plugins/cmdb,/opt/nacos/plugins/selector -Dnacos.home=/opt/nacos -jar /opt/nacos/target/nacos-server.jar --spring.config.additional-location=file:/opt/nacos/conf/ --logging.config=/opt/nacos/conf/nacos-logback.xml --server.max-http-header-size=524288 nacos.nacos
3000 15416 15395 3 16:05 ? 00:16:22 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.332.b09-1.el7_9.x86_64/bin/java -server -Xms909M -Xmx909M -Xmn200M -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:SurvivorRatio=8 -verbose:gc -Xloggc:/dev/shm/mq_gc_%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m -XX:-OmitStackTraceInFastThrow -XX:+AlwaysPreTouch -XX:MaxDirectMemorySize=909M -XX:-UseLargePages -XX:-UseBiasedLocking -Djava.ext.dirs=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.332.b09-1.el7_9.x86_64/jre/lib/ext:/home/rocketmq/rocketmq-4.9.4/bin/../lib -cp .:/home/rocketmq/rocketmq-4.9.4/bin/../conf: org.apache.rocketmq.broker.BrokerStartup -c ../conf/broker.conf
root 16809 10683 0 22:56 pts/0 00:00:00 grep --color=auto java
ps -aux | grep java 的内容:
root 4002 0.0 0.0 4324 8 pts/0 Ss+ 14:56 0:00 sh -c java $JAVA_OPTS -jar /rocketmq-dashboard.jar
root 4628 0.4 6.8 2983732 128476 pts/0 Sl+ 14:56 1:58 java -Drocketmq.namesrv.addr=192.168.58.128:9876 -jar /rocketmq-dashboard.jar
3000 4997 0.2 7.5 3445320 141516 ? Sl 14:56 1:08 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.332.b09-1.el7_9.x86_64/bin/java -server -Xms909M -Xmx909M -Xmn200M -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:-UseParNewGC -verbose:gc -Xloggc:/dev/shm/rmq_srv_gc.log -XX:+PrintGCDetails -XX:-OmitStackTraceInFastThrow -XX:-UseLargePages -Djava.ext.dirs=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.332.b09-1.el7_9.x86_64/jre/lib/ext:/home/rocketmq/rocketmq-4.9.4/bin/../lib -cp .:/home/rocketmq/rocketmq-4.9.4/bin/../conf: org.apache.rocketmq.namesrv.NamesrvStartup
root 10726 0.9 27.1 3533976 506316 pts/0 Sl 15:44 4:15 /opt/jdk8/bin/java -Djava.ext.dirs=/opt/jdk8/jre/lib/ext:/opt/jdk8/lib/ext -Xms512m -Xmx512m -Xmn256m -Dnacos.standalone=true -Dnacos.member.list= -Xloggc:/opt/nacos/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Dloader.path=/opt/nacos/plugins,/opt/nacos/plugins/health,/opt/nacos/plugins/cmdb,/opt/nacos/plugins/selector -Dnacos.home=/opt/nacos -jar /opt/nacos/target/nacos-server.jar --spring.config.additional-location=file:/opt/nacos/conf/ --logging.config=/opt/nacos/conf/nacos-logback.xml --server.max-http-header-size=524288 nacos.nacos
3000 15416 3.9 17.3 5065612 322680 ? Sl 16:05 16:31 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.332.b09-1.el7_9.x86_64/bin/java -server -Xms909M -Xmx909M -Xmn200M -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:SurvivorRatio=8 -verbose:gc -Xloggc:/dev/shm/mq_gc_%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m -XX:-OmitStackTraceInFastThrow -XX:+AlwaysPreTouch -XX:MaxDirectMemorySize=909M -XX:-UseLargePages -XX:-UseBiasedLocking -Djava.ext.dirs=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.332.b09-1.el7_9.x86_64/jre/lib/ext:/home/rocketmq/rocketmq-4.9.4/bin/../lib -cp .:/home/rocketmq/rocketmq-4.9.4/bin/../conf: org.apache.rocketmq.broker.BrokerStartup -c ../conf/broker.conf
root 16817 0.0 0.0 112816 980 pts/0 S+ 23:00 0:00 grep --color=auto java
写在最后
JPS总体上是一些其他JVM工具的引导,比如Jstat需要依赖JPS获取的进程号进行性能优化和问题排查,JConsole
工具来监控JVM的运行情况。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。