JPS 命令詳細解釋

Xander發表於2022-12-28

引言

JPS命令是日常開發過程中經常遇到的命令。使用起來也非常簡單,本節內容主要翻譯Oracel官方的JPS說明,以及相關的實現原理分析,最後介紹一些JPS無法獲取到JAVA程式的原因排查。

官方文件翻譯

原文:jps - Java Virtual Machine Process Status Tool (oracle.com)

SYNOPSIS(簡介)

jps [ options] [ hostid ]

PARAMETERS

options

Command-line options.

hostid

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.

用於與遠端伺服器通訊的預設埠。如果省略 hostnameprotocol 指定了最佳化的本地協議,則會忽略 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的程式李米娜,使用特殊的協議和埠。

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檔案。
  • 臨時檔案丟失,被刪除或是定期清理。比較常見的檔案丟失原因可能是定時刪除臨時目錄的工具,比如crontabredhattmpwatchubuntutmpreaper等等

    用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的執行情況。

相關文章