Java系统工具jps原理解析

Java系统工具jps原理解析


简介

当我们需要获取当前正在运行的Java进程时,我们可以通过操作系统自带的工具来筛选,如ps和netstat等。不过Java也提供了通用的工具来实现该功能,而且能够提供更加详细的信息。jps是Java Virtual Machine Process Status Too的简称,可以用来获取当前用户系统中的Java进程。

使用

jps的命令格式为 jps [ options ] [ hostid ],具体形式如下:

usage: jps [-help]
       jps [-q] [-mlvV] [<hostid>]

Definitions:
    <hostid>:      <hostname>[:<port>]

其中的参数解释如下(其中mlvV可以组合使用):

  • q:只显示Java进程的lvmid,不显示class名称、jar文件名和传递给main 方法的参数等
  • m:输出传递给main方法的参数
  • l:输出应用程序入口函数的完整package名或者应用程序的jar文件完整路径名
  • v:输出传递给JVM的参数
  • V:通过标识文件来输出传递给JVM的参数(即.hotspotrc文件或者由参数-XX:Flags=确定的文件)
  • hostid:需要查看Java进程的系统,由主机名和端口号来确定

注:查看本地Java进程不需要hostid参数,如果要查看远程进程,需要在远程主机提供jstatd服务和RMI绑定,稍后会介绍jstatd

jps的输出结果格式为 lvmid [ [ classname | JARfilename | "Unknown"] [ arg* ] [ jvmarg* ] ],下面是在Ubuntu系统中进行的测试实例:

[email protected]:~$ jps
3540 org.eclipse.equinox.launcher_1.3.0.v20140415-2008.jar
3669 Main
4535 Jps

[email protected]:~$ jps -m
4548 Jps -m
3540 org.eclipse.equinox.launcher_1.3.0.v20140415-2008.jar -os linux -ws gtk -arch x86_64 -showsplash /usr/share/eclipse//plugins/org.eclipse.platform_4.4.2.v20150204-1700/splash.bmp -launcher /usr/share/eclipse/eclipse -name Eclipse --launcher.library /usr/share/eclipse//plugins/org.eclipse.equinox.launcher.gtk.linux.x86_64_1.1.200.v20150204-1316/eclipse_1607.so -startup /usr/share/eclipse//plugins/org.eclipse.equinox.launcher_1.3.0.v20140415-2008.jar --launcher.appendVmargs -exitdata 220011 -product org.eclipse.epp.package.java.product -vm /usr/bin/java -vmargs -Dosgi.requiredJavaVersion=1.6 -XX:MaxPermSize=256m -Xms40m -Xmx512m -jar /usr/share/eclipse//plugins/org.eclipse.equinox.launcher_1.3.0.v20140415-2008.jar
3669 Main

[email protected]:~$ jps -l
4738 sun.tools.jps.Jps
3540 /usr/share/eclipse//plugins/org.eclipse.equinox.launcher_1.3.0.v20140415-2008.jar
3669 local.Main

[email protected]:~$ jps -v
3540 org.eclipse.equinox.launcher_1.3.0.v20140415-2008.jar -Dosgi.requiredJavaVersion=1.6 -XX:MaxPermSize=256m -Xms40m -Xmx512m
3669 Main -Dfile.encoding=UTF-8
4907 Jps -Dapplication.home=/usr/lib/jvm/jdk1.8.0_45 -Xms8m

原理

下面解析一下jps如何实现的,参看的代码来自openjdk7。我们从入口类Jps.java看起,这个类中就一个Main方法,核心的代码如下图所示,首先从特定的主机上获取正在运行的Java进程,然后对这些进程进行输出,如果有参数的话还要额外输出参数需要输出的信息。从主机获取Java进程主要有分为两种,一种是本地的,另一种通过RMI远程调用的。

            HostIdentifier hostId = arguments.hostId();
            MonitoredHost monitoredHost =
                    MonitoredHost.getMonitoredHost(hostId);

            // 从特定主机上获取Java进程
            Set jvms = monitoredHost.activeVms();

            for (Iterator j = jvms.iterator(); j.hasNext(); /* empty */ ) {
                StringBuilder output = new StringBuilder();
                Throwable lastError = null;

                int lvmid = ((Integer)j.next()).intValue();

                output.append(String.valueOf(lvmid));

                if (arguments.isQuiet()) {
                    System.out.println(output);
                    continue;
                }

                MonitoredVm vm = null;
                String vmidString = "//" + lvmid + "?mode=r";

                String errorString = null;

                // 处理其他参数
            }

我们主要看一下从本地获取Java进程的代码,monitoredHost.activeVms()方法最后通过LocalVmManager中的activeVms()来实现。通过看下面的代码,我们容易发现最后返回的lvmid集合jvmSet中的元素都是通过类似PerfDataFile.getLocalVmId(files[j])的方法添加的,而这些files的文件夹也是通过类PerfDataFile中的函数PerfDataFile.getTempDirectory(userName)得来的。细心的同学应该发现文件夹的结构会根据有无用户名而发生变化,而通过PerfDataFile中的代码可以发现该文件夹下的文件名也有着两种不同的标准。接下去我们继续看类PerfDataFile。

public synchronized Set<Integer> activeVms() {
        /*
         * This method is synchronized because the Matcher object used by
         * fileFilter is not safe for concurrent use, and this method is
         * called by multiple threads. Before this method was synchronized,
         * we‘d see strange file names being matched by the matcher.
         */
        Set<Integer> jvmSet = new HashSet<Integer>();

        if (! tmpdir.isDirectory()) {
            return jvmSet;
        }

        if (userName == null) {
            /*
             * get a list of all of the user temporary directories and
             * iterate over the list to find any files within those directories.
             */
            File[] dirs = tmpdir.listFiles(userFilter);

            for (int i = 0 ; i < dirs.length; i ++) {
                if (!dirs[i].isDirectory()) {
                    continue;
                }

                // get a list of files from the directory
                File[] files = dirs[i].listFiles(fileFilter);

                if (files != null) {
                    for (int j = 0; j < files.length; j++) {
                        if (files[j].isFile() && files[j].canRead()) {
                            jvmSet.add(new Integer(
                                    PerfDataFile.getLocalVmId(files[j])));
                        }
                    }
                }
            }
        } else {
            /*
             * Check if the user directory can be accessed. Any of these
             * conditions may have asynchronously changed between subsequent
             * calls to this method.
             */

            // get the list of files from the specified user directory
            File[] files = tmpdir.listFiles(fileFilter);

            if (files != null) {
                for (int j = 0; j < files.length; j++) {
                    if (files[j].isFile() && files[j].canRead()) {
                        jvmSet.add(new Integer(
                                PerfDataFile.getLocalVmId(files[j])));
                    }
                }
            }
        }

        // look for any 1.4.1 files
        File[] files = tmpdir.listFiles(tmpFileFilter);
        if (files != null) {
            for (int j = 0; j < files.length; j++) {
                if (files[j].isFile() && files[j].canRead()) {
                    jvmSet.add(new Integer(
                            PerfDataFile.getLocalVmId(files[j])));
                }
            }
        }

        return jvmSet;
    }
}

类PerfDataFile提供了Java进程与文件相互转换的方法。从该类中可以找到各系统下这些文件的对应位置,如方法getTempDirectory所示,该临时目录的路径如下: tmpDirName + dirNamePrefix + user + File.separator,其中tmpDirName为Java io对应的临时文件,dirNamePrefix固定为”hsperfdata_”,user是系统用户名,File.separator是文件分隔符。在Windows系统下,默认路径为 C:\Users\username\AppData\Local\Temp\hsperfdata_username,而在Linux系统下,默认路径为/tmp/hsperfdata_username。在这些文件夹下的文件会在对应的Java进程结束被清除。方法getLocalVmId是用来从进程文件得到lvmid,可以看出1.4.2版本及以后的HotSpot虚拟机直接以lvmid为名字,之前的版本中拥有分隔符。

注:Windows中的临时文件可能为隐藏文件

    public static String getTempDirectory(String user) {
        return tmpDirName + dirNamePrefix + user + File.separator;
    }

    public static int getLocalVmId(File file) {
        int lvmid = 0;

        try {
            // try 1.4.2 and later format first
            return Integer.parseInt(file.getName());
        } catch (NumberFormatException e) { }

        // now try the 1.4.1 format
        String name = file.getName();
        if (name.startsWith(dirNamePrefix)) {
            int first = name.indexOf(‘_‘);
            int last = name.lastIndexOf(‘_‘);
            try {
                if (first == last) {
                    return Integer.parseInt(name.substring(first + 1));
                } else {
                    return Integer.parseInt(name.substring(first + 1, last));
                }
            } catch (NumberFormatException e) { }
        }
        throw new IllegalArgumentException("file name does not match pattern");
    }

总结

本文主要讲解了jps的使用方法及它的实现原理是什么。进一步可以思考如下一些问题:

  1. 这些进程对应的文件是什么时候写入对应文件夹中的?
  2. jps的参数是如何解析文件中的内容的?

感兴趣的同学可以继续通过阅读源码来深入了解。

时间: 2024-10-07 01:00:55

Java系统工具jps原理解析的相关文章

【Java】Servlet 工作原理解析

Web 技术成为当今主流的互联网 Web 应用技术之一,而 Servlet 是 Java Web 技术的核心基础.因而掌握 Servlet 的工作原理是成为一名合格的 Java Web 技术开发人员的基本要求.本文将带你认识 Java Web 技术是如何基于 Servlet 工作,你将知道:以 Tomcat 为例了解 Servlet 容器是如何工作的?一个 Web 工程在 Servlet 容器中是如何启动的? Servlet 容器如何解析你在 web.xml 中定义的 Servlet ?用户的请

大神必修课系列之java 分布式架构的原理解析

分布式术语 1.1. 异常 服务器宕机 内存错误.服务器停电等都会导致服务器宕机,此时节点无法正常工作,称为不可用. 服务器宕机会导致节点失去所有内存信息,因此需要将内存信息保存到持久化介质上. 网络异常 有一种特殊的网络异常称为--网络分区 ,即集群的所有节点被划分为多个区域,每个区域内部可以通信,但是区域之间无法通信. 磁盘故障 磁盘故障是一种发生概率很高的异常. 使用冗余机制,将数据存储到多台服务器. 1.2. 超时 在分布式系统中,一个请求除了成功和失败两种状态,还存在着超时状态. 可以

车牌识别停车场计费系统的工作原理解析

今天主要来分享下车牌识别以及车牌识别停车计费系统的一些知识. 关键核心:车牌识别.停车场计费系统.停车管理系统.OCR识别 一.首先那停车收费系统由哪些组成的呢? 从产品形态上来看停车收费系统由软件(车牌识别.计费系统)和硬件(相机.立柱.显示屏.道闸.地感等)这两方面来组成. 1.车牌识别软件系统 车牌识别系统(Vehicle License Plate Recognition,VLPR) 是指能够检测到受监控路面的车辆并自动提取车辆牌照信息(含汉字字符.英文字母.阿拉伯数字及号牌颜色)进行处

Java序列化工具对比

1. Java序列化工具技术原理比较 Binary Formats & language-specific ones JavaBuiltIn(java原生).JavaManual(根据成员变量类型,手工写).FstSerliazation.Kryo Binary formats-generic language-unspecific ones Protobuf(Google).Thrift(Facebook). AvroGeneric.Hessian JSON Format Jackson.Gs

java学习之常用Java Profiling工具的分析与比较

在 Java 程序的开发过程中,不可避免地会遇到内存使用.性能瓶颈等问题.Java Profiler 工具能帮助开发人员快速.有效地定位这些问题,因此成为了 Java 开发过程中的一个重要工具.目前市场上的 Java Profiler 工具种类繁多,本文将对目前比较常见的几种工具进行简要介绍,并从功能.性能等角度作比较,从而帮助 Java 程序员选择合适的 Java Profiler 工具. 本文主要分为三个部分:第一部分简要介绍 Java Profiler 工具的原理:第二部分对目前常见的 J

Java 并发工具CountDownLatch和CyclicBarrier 原理解析

[TOC] Java 并发工具CountDownLatch和CyclicBarrier 原理解析 一,简介 CountDownLatch 允许一个或者多个线程等待其他线程完成操作. CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier).它要做的事情是,让一组线程达到一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行. 二,代码演示 CountDownLatchDemo public class Co

Java类加载原理解析

1       基本信息 摘要: 每个java开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这背后就涉及到了java技术体系中的类加载.Java的类加载机制是java技术体系中比较核心的部分,虽然和大部分开发人员直接打交道不多,但是对其背后的机理有一定理解有助于排查程序中出现的类加载失败等技术问题,对理解java虚拟机的连接模型和java语言的动态性都有很大帮助. 由于关于java类加载的内容较多,所以打算分三篇文章简述一下: 第一篇:java类

Android中Xposed框架篇---基于Xposed的一款脱壳神器ZjDroid工具原理解析

一.前言 在前文中我们介绍了如何使用Xposed框架修改地理位置信息来进行自身的隐藏功能,本文将继续介绍Xposed框架的另外一个功能就是实现应用的简单脱壳,其实说是Xposed的作用其实也不是,主要是模块编写的好就可以了,主要是利用Xposed的牛逼Hook技术实现的,下面就先来介绍一下这个脱壳模块工具ZjDroid的原理,因为他是开源的,所以咋们直接分析源码即可,源码的下载地址:https://github.com/halfkiss/ZjDroid 不过可惜的时候他只公开了Java层的代码,

Java效率工具Lombok使用与原理

Java效率工具Lombok使用与原理 我个人觉得 Lombok是一个优化Java代码以及提升开发效率不错的工具.Lombok 的Github地址为:https://github.com/rzwitserloot/lombok ,目前已经有7.9k star.Lombok主要为Java提供了不少语法糖,其中很多的设计都符合<Effective Java>所描述的Java编程最佳实践.Lombok 的学习成本很低,几乎可以直接上手,但是在团队开发的时候要注意,如果团队开发要使用这种工具一定要沟通