【问题标题】:How can a Java program get its own process ID?Java 程序如何获得自己的进程 ID?
【发布时间】:2010-09-07 08:13:22
【问题描述】:

如何获取我的 Java 进程的 ID?

我知道有几种依赖于平台的 hack,但我更喜欢更通用的解决方案。

【问题讨论】:

标签: java pid


【解决方案1】:

不存在可以保证在所有 jvm 实现中工作的独立于平台的方式。 ManagementFactory.getRuntimeMXBean().getName() 看起来是最好的(最接近的)解决方案,通常包括 PID。它很短,可能适用于广泛使用的每个实现。

在 linux+windows 上,它返回一个类似 "12345@hostname" 的值(12345 是进程 ID)。请注意according to the docs,但不能保证此值:

返回代表正在运行的 Java 虚拟机的名称。这 返回的名称字符串可以是任意字符串和 Java 虚拟 机器实现可以选择嵌入特定于平台的有用 返回的名称字符串中的信息。每个正在运行的虚拟机 可以有不同的名称。

在 Java 9中可以使用新的process API

long pid = ProcessHandle.current().pid();

【讨论】:

【解决方案2】:

您可以使用JNA。不幸的是,目前还没有通用的 JNA API 来获取当前进程 ID,但是每个平台都非常简单:

窗口

确保你有jna-platform.jar然后:

int pid = Kernel32.INSTANCE.GetCurrentProcessId();

Unix

声明:

private interface CLibrary extends Library {
    CLibrary INSTANCE = (CLibrary) Native.loadLibrary("c", CLibrary.class);   
    int getpid ();
}

然后:

int pid = CLibrary.INSTANCE.getpid();

Java 9

在 Java 9 下,新的 process API 可用于获取当前进程 ID。首先获取当前进程的句柄,然后查询 PID:

long pid = ProcessHandle.current().pid();

【讨论】:

  • +1 但恐怕在安全受限的环境中它不应该工作(tomcat、WebLogic 等)。
  • getpid() 解决方案也适用于 OS X,通过 JNA 调用“系统”库。
  • 但是,我得到了 jdk9 的 error: cannot find symbol
【解决方案3】:

这是一个后门方法,可能不适用于所有虚拟机,但应该适用于 linux 和 windows (original example here):

java.lang.management.RuntimeMXBean runtime = 
    java.lang.management.ManagementFactory.getRuntimeMXBean();
java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm");
jvm.setAccessible(true);
sun.management.VMManagement mgmt =  
    (sun.management.VMManagement) jvm.get(runtime);
java.lang.reflect.Method pid_method =  
    mgmt.getClass().getDeclaredMethod("getProcessId");
pid_method.setAccessible(true);

int pid = (Integer) pid_method.invoke(mgmt);

【讨论】:

  • 非常好,刚刚验证它可以在 JRockit 和 Hotspot JVM 上运行。
  • 不错的解决方法。我将假设有充分的理由说明此方法(以及类中的其他方法)不公开且易于访问,我很想知道它是什么。
  • Oracle Java 团队已经宣布他们打算隐藏所有非 Java 包(即 sun.*),我相信从 Java 10(计划于 2018 年 - 可能是 Java 9)开始。如果您有与上述类似的实现,您可能想找出一个替代方案,这样您的代码就不会中断。
  • 对我也不起作用,因为 sun.* 包可能已被删除或无法访问(VMManagement 无法解析为一种类型)。
  • 由于反射的工作方式,不需要访问 sun.management.*。只需对jvm.get(runtime) 返回的对象执行getDeclaredMethod
【解决方案4】:

试试Sigar。非常广泛的 API。 Apache 2 许可证。

private Sigar sigar;

public synchronized Sigar getSigar() {
    if (sigar == null) {
        sigar = new Sigar();
    }
    return sigar;
}

public synchronized void forceRelease() {
    if (sigar != null) {
        sigar.close();
        sigar = null;
    }
}

public long getPid() {
    return getSigar().getPid();
}

【讨论】:

  • 如果您解释了如何为此使用 SIGAR,您将获得更多支持
  • 链接已损坏。你举了一个如何使用它的例子,但是它有一个maven依赖吗?
  • github.com/hyperic/sigar 似乎是一个可用的位置;这也表明它是带有 Java 绑定的本机代码,这可能使其在许多上下文中不太适合。
【解决方案5】:

以下方法尝试从java.lang.management.ManagementFactory中提取PID:

private static String getProcessId(final String fallback) {
    // Note: may fail in some JVM implementations
    // therefore fallback has to be provided

    // something like '<pid>@<hostname>', at least in SUN / Oracle JVMs
    final String jvmName = ManagementFactory.getRuntimeMXBean().getName();
    final int index = jvmName.indexOf('@');

    if (index < 1) {
        // part before '@' empty (index = 0) / '@' not found (index = -1)
        return fallback;
    }

    try {
        return Long.toString(Long.parseLong(jvmName.substring(0, index)));
    } catch (NumberFormatException e) {
        // ignore
    }
    return fallback;
}

例如,只需致电getProcessId("&lt;PID&gt;")

【讨论】:

  • VisualVM 使用类似的代码来获取自己的 PID,检查 com.sun.tools.visualvm.application.jvm.Jvm#ApplicationSupport#createCurrentApplication()。他们是专家,因此它看起来像是可靠的跨平台解决方案。
  • @Espinosa VisualVM 仅支持在 OpenJDK/Oracle/GraalVM JVM 上运行(截至 2020 年)。这意味着他们可以做出相应的假设。如果你这样做,你就会变得依赖于供应商,如果在 J9 上运行,你的代码可能会中断。
【解决方案6】:

对于较旧的 JVM,在 linux 中...

private static String getPid() throws IOException {
    byte[] bo = new byte[256];
    InputStream is = new FileInputStream("/proc/self/stat");
    is.read(bo);
    for (int i = 0; i < bo.length; i++) {
        if ((bo[i] < '0') || (bo[i] > '9')) {
            return new String(bo, 0, i);
        }
    }
    return "-1";
}

【讨论】:

  • 如果您要这样做,那么只需执行int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());。为什么会有额外的回旋?
  • 对于好奇的人,@David 评论中的技巧确实有效。有人可以说为什么吗? getCanonicalFile() 方法中的某种魔法将“self”转换为 PID?
  • getCanonicalFile() 解析符号链接 /proc/self/ -> /proc/1234,试试 ls -al /proc/self
  • @DavidCitron +1!你是对的,我没有想到 getCanonicalFile :-)
【解决方案7】:

从 Java 9 开始,有一个方法 Process.getPid() 返回进程的本地 ID:

public abstract class Process {

    ...

    public long getPid();
}

要获取当前Java进程的进程ID,可以使用ProcessHandle接口:

System.out.println(ProcessHandle.current().pid());

【讨论】:

  • 你能解释一下如何在 Java 9 中为当前正在运行的进程获取一个 Process 实例吗?
  • 2016 年使用 Java 9 的绝佳答案!您可以添加指向 javadocs 的链接吗?谢谢
【解决方案8】:

您可以在 GitHub 上查看我的项目:JavaSysMon。它提供进程 ID 和一堆其他东西(CPU 使用率、内存使用率)跨平台(目前是 Windows、Mac OSX、Linux 和 Solaris)

【讨论】:

  • 是否可以获取Java启动的进程的PID?或者启动一个“你的方式”来解决这个问题?
【解决方案9】:

在 Scala 中:

import sys.process._
val pid: Long = Seq("sh", "-c", "echo $PPID").!!.trim.toLong

在 Java 9 发布之前,这应该会为您提供 Unix 系统上的解决方法。 (我知道,这个问题是关于 Java 的,但由于 Scala 没有等效的问题,我想把这个留给可能会遇到同样问题的 Scala 用户。)

【讨论】:

  • (在 Linux 和 macOS 上看起来不错,但我不知道它是否可以在 Windows 上运行)
【解决方案10】:
java.lang.management.ManagementFactory.getRuntimeMXBean().getName().split("@")[0]

【讨论】:

    【解决方案11】:

    为了完整起见,Spring Boot 中有一个用于

    的包装器
    String jvmName = ManagementFactory.getRuntimeMXBean().getName();
    return jvmName.split("@")[0];
    

    解决方案。如果需要整数,则可以总结为单行:

    int pid = Integer.parseInt(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
    

    如果有人已经使用 Spring boot,她/他可能会使用 org.springframework.boot.ApplicationPid

    ApplicationPid pid = new ApplicationPid();
    pid.toString();
    

    toString() 方法打印 pid 或 '???'。

    其他答案中已经讨论了使用 ManagementFactory 的注意事项。

    【讨论】:

      【解决方案12】:
      public static long getPID() {
          String processName = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
          if (processName != null && processName.length() > 0) {
              try {
                  return Long.parseLong(processName.split("@")[0]);
              }
              catch (Exception e) {
                  return 0;
              }
          }
      
          return 0;
      }
      

      【讨论】:

        【解决方案13】:

        除了其他解决方案之外,我正在添加这个。

        使用 Java 10,获取 process id

        final RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
        final long pid = runtime.getPid();
        out.println("Process ID is '" + pid);
        

        【讨论】:

          【解决方案14】:

          我发现的最新消息是有一个名为sun.java.launcher.pid系统属性 至少在Linux 上可用。我的计划是使用它,如果没有找到使用JMX bean

          【讨论】:

          • 在我使用 Ubuntu 16.04 和 Java 8 的情况下,它返回 null
          【解决方案15】:

          这取决于您从哪里寻找信息。

          如果您正在从控制台查找信息,您可以使用 jps 命令。该命令提供类似于 Unix ps 命令的输出,并且由于我相信 1.5 而随 JDK 一起提供

          如果您从流程来看,RuntimeMXBean(如 Wouter Coekaerts 所说)可能是您的最佳选择。使用 Sun JDK 1.6 u7 在 Windows 上 getName() 的输出格式为 [PROCESS_ID]@[MACHINE_NAME]。但是,您可以尝试执行 jps 并从中解析结果:

          String jps = [JDK HOME] + "\\bin\\jps.exe";
          Process p = Runtime.getRuntime().exec(jps);
          

          如果在没有选项的情况下运行,输出应该是进程 ID 后跟名称。

          【讨论】:

          【解决方案16】:

          这是 JConsole 的代码,可能还有 jps 和 VisualVM 使用。它利用来自的类 sun.jvmstat.monitor.* 包,来自tool.jar

          package my.code.a003.process;
          
          import sun.jvmstat.monitor.HostIdentifier;
          import sun.jvmstat.monitor.MonitorException;
          import sun.jvmstat.monitor.MonitoredHost;
          import sun.jvmstat.monitor.MonitoredVm;
          import sun.jvmstat.monitor.MonitoredVmUtil;
          import sun.jvmstat.monitor.VmIdentifier;
          
          
          public class GetOwnPid {
          
              public static void main(String[] args) {
                  new GetOwnPid().run();
              }
          
              public void run() {
                  System.out.println(getPid(this.getClass()));
              }
          
              public Integer getPid(Class<?> mainClass) {
                  MonitoredHost monitoredHost;
                  Set<Integer> activeVmPids;
                  try {
                      monitoredHost = MonitoredHost.getMonitoredHost(new HostIdentifier((String) null));
                      activeVmPids = monitoredHost.activeVms();
                      MonitoredVm mvm = null;
                      for (Integer vmPid : activeVmPids) {
                          try {
                              mvm = monitoredHost.getMonitoredVm(new VmIdentifier(vmPid.toString()));
                              String mvmMainClass = MonitoredVmUtil.mainClass(mvm, true);
                              if (mainClass.getName().equals(mvmMainClass)) {
                                  return vmPid;
                              }
                          } finally {
                              if (mvm != null) {
                                  mvm.detach();
                              }
                          }
                      }
                  } catch (java.net.URISyntaxException e) {
                      throw new InternalError(e.getMessage());
                  } catch (MonitorException e) {
                      throw new InternalError(e.getMessage());
                  }
                  return null;
              }
          }
          

          有几个问题:

          • tool.jar 是随 Oracle JDK 而不是 JRE 分发的库!
          • 您无法从 Maven 存储库中获取 tool.jar;用 Maven 配置它有点棘手
          • tool.jar 可能包含平台相关(本机?)代码,因此不容易 可分发
          • 它的运行假设所有(本地)运行的 JVM 应用程序都是“可监控的”。看起来像 从 Java 6 开始,所有应用程序通常都是(除非您主动配置相反)
          • 它可能只适用于 Java 6+
          • Eclipse 没有发布主类,所以你不会轻易得到 Eclipse PID MonitoredVmUtil 中的错误?

          更新:我刚刚仔细检查了 JPS 使用这种方式,即 Jvmstat 库(tool.jar 的一部分)。所以不需要调用 JPS 作为外部进程,直接调用 Jvmstat 库,如我的示例所示。您还可以通过这种方式获取在 localhost 上运行的所有 JVM 的列表。 见 JPSsource code:

          【讨论】:

          • 如果 mainClass.getName() 不起作用,请尝试使用 mainClass.getCanonicalName();
          【解决方案17】:

          基于 Ashwin Jayaprakash 的 answer (+1) 关于 Apache 2.0 许可的SIGAR,这是我如何使用它来仅获取当前进程的 PID:

          import org.hyperic.sigar.Sigar;
          
          Sigar sigar = new Sigar();
          long pid = sigar.getPid();
          sigar.close();
          

          尽管它不适用于所有平台,但它确实适用于 Linux, Windows, OS X and various Unix platforms as listed here

          【讨论】:

            【解决方案18】:

            我知道这是一个旧线程,但我想指出用于获取 PID(以及在运行时对 Java 进程的其他操作)的 API 正在添加到 JDK 9 中的 Process 类中:http://openjdk.java.net/jeps/102

            【讨论】:

            • 哇,真快。只用了大约七年! ?
            【解决方案19】:

            您可以在JNR-Posix 中尝试getpid()

            它有一个 Windows POSIX 包装器,可以从 libc 中调用 getpid()。

            【讨论】:

              【解决方案20】:

              我发现了一个可能有点边缘化的解决方案,我没有在 Windows 10 以外的其他操作系统上尝试过,但我认为它值得注意。

              如果您发现自己在使用 J2V8 和 nodejs,您可以运行一个简单的 javascript 函数,返回 java 进程的 pid。

              这是一个例子:

              public static void main(String[] args) {
                  NodeJS nodeJS = NodeJS.createNodeJS();
                  int pid = nodeJS.getRuntime().executeIntegerScript("process.pid;\n");
                  System.out.println(pid);
                  nodeJS.release();
              }
              

              【讨论】:

                【解决方案21】:

                这是我的解决方案:

                public static boolean isPIDInUse(int pid) {
                
                        try {
                
                            String s = null;
                            int java_pid;
                
                            RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean();
                            java_pid = Integer.parseInt(rt.getName().substring(0, rt.getName().indexOf("@")));
                
                            if (java_pid == pid) {
                                System.out.println("In Use\n");
                                return true;
                            }
                        } catch (Exception e) {
                            System.out.println("Exception:  " + e.getMessage());
                        }
                        return false;
                    }
                

                【讨论】:

                • 这不检查pid是否正在使用,但如果pid等于当前进程pid
                • 什么是正确的解决方案,以便我可以更新我的代码?
                【解决方案22】:

                这是我有类似要求时使用的。这将正确确定 Java 进程的 PID。让您的 java 代码在预定义的端口号上生成服务器,然后执行操作系统命令以找出侦听端口的 PID。对于 Linux

                netstat -tupln | grep portNumber
                

                【讨论】:

                • 如果你正在调用 shell 脚本,你还不如做一些更简单的事情,比如 bash -c 'echo $PPID' 或 /proc answers above
                猜你喜欢
                • 1970-01-01
                • 2011-06-03
                • 2016-04-15
                • 2014-07-01
                • 1970-01-01
                • 1970-01-01
                • 2011-06-05
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多