Java程序如何获得自己的进程ID?


Answers:


322

不存在可以保证在所有jvm实现中都能工作的与平台无关的方式。 ManagementFactory.getRuntimeMXBean().getName()看起来是最好的(最近的)解决方案。它很短,并且可能在广泛使用的每个实现中都起作用。

在linux + windows上,它返回一个类似12345@hostname12345是进程ID)的值。但是请注意,根据docs,不能保证该值:

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

在Java 9中,可以使用新的流程API

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

2
这个解决方案确实很脆弱。请参阅下面有关Hyperic Sigar的答案。
Michael Klishin 2014年

该pid可以很好地写在锁定文件上,如stackoverflow.com/a/9020391/1422630
Aquarius Power

111

您可以使用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下,新的流程API可用于获取当前流程ID。首先,您要获取当前进程的句柄,然后查询PID:

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

4
+1但是我担心在安全受限的环境中,它不起作用(tomcat,WebLogic等)。
ATorras

getpid()解决方案还适用于OS X,通过JNA调用“系统”库。
丹尼尔·维迪斯

但是,我得到error: cannot find symboljdk9
skytree

56

这是一种后门方法,该方法可能不适用于所有VM,但应同时在Linux和Windows上运行(此处为原始示例):

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);

2
非常好,刚刚验证它可以在JRockit和Hotspot JVM上使用。
Drupad Panchal 2012年

1
不错的解决方法。我将假设有充分的理由说明这种方法(以及该类中的其他方法)不公开且不易访问,并且我很想知道它是什么。
fragorl 2014年

2
Oracle Java团队已经宣布,他们打算隐藏所有非Java软件包(例如sun。*),我相信将从Java 10(计划于2018年-也许是Java 9)开始。如果您具有与上述类似的实现,则可能需要找出一种替代方案,以使您的代码不会中断。
hfontanez 2014年

由于sun。*软件包可能已删除或不可访问(VMManagement无法解析为一种类型),因此对我不起作用。
Mike Stoddart,2015年

由于反射的工作方式,因此不需要访问sun.management。*。只需getDeclaredMethod对返回的对象执行jvm.get(runtime)
Andrew T Finnell

36

尝试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();
}

6
如果您解释了如何使用SIGAR,则您会获得更多的好评
Brad Mace

1
链接断开。您给出了如何使用它的示例,但是是否有Maven依赖项?
Jules

github.com/hyperic/sigar似乎是一个可用的位置;这也表明它是带有Java绑定的本机代码,这可能会使它不再是许多上下文的解决方案。
Zastai

26

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

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("<PID>")例如,只需致电。


6
VisualVM使用类似的代码来获取自身的PID,请检查com.sun.tools.visualvm.application.jvm.Jvm#ApplicationSupport#createCurrentApplication()。他们是专家,因此看起来像是可靠的跨平台解决方案。
Espinosa 2013年

@Espinosa VisualVM仅支持在OpenJDK / Oracle / GraalVM JVM上运行(截至2020年)。这意味着他们可以做出相应的假设。如果执行相同的操作,则将成为供应商依赖的,并且如果在例如J9上运行,则代码可能会中断。
托尔比约恩Ravn的安徒生

24

对于较早的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";
}

52
如果您要这样做,那就去做吧int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());。为什么要额外旋转呢?
David Citron

2
出于好奇,@ David评论中的技巧实际上是有效的。有人可以说为什么吗?getCanonicalFile()将“自我”转换为PID 的方法中是否存在某种魔术?
GreenGiant 2012年

7
getCanonicalFile()解析符号链接/ proc / self /-> / proc / 1234,尝试ls -al / proc / self
Michael

2
@DavidCitron +1!您是对的,我没想到在getCanonicalFile中:-)
ggrandes 2013年

18

您可以签出我的项目:GitHub上的JavaSysMon。它提供了进程ID和跨平台(目前为Windows,Mac OSX,Linux和Solaris)的其他功能(CPU使用率,内存使用率)


是否可以通过Java启动进程的PID?还是以“您的方式”启动流程来解决此问题?
Panayotis

17

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

public abstract class Process {

    ...

    public long getPid();
}

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

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

2
您能解释一下如何在Java 9中获取当前正在运行的进程的Process实例吗?
2015年

在2016年使用Java 9的绝佳答案!您可以添加指向javadocs的链接吗?谢谢
crazyGuy '16

8

在Scala中:

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

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


7

为了完整起见,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的注意事项。


7
java.lang.management.ManagementFactory.getRuntimeMXBean().getName().split("@")[0]

6
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;
}

5

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


就我的Ubuntu 16.04和Java 8而言,它返回null
david.perez

4

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

如果要从控制台中查找信息,则可以使用jps命令。该命令给出的输出类似于Unix ps命令,并且随JDK一起提供,因为我相信1.5

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

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

如果不带任何选项运行,则输出应为进程ID,后跟名称。


1
JPS工具使用jvmstat库,该库是tools.jar的一部分。检查我的示例或查看JPS源代码:grepcode.com/file_/repository.grepcode.com/java/root/jdk/…。无需将JPS作为外部进程调用,直接使用jvmstat库。
Espinosa 2013年

4

这是代码JConsole,并且可能是jps和VisualVM使用。它利用sun.jvmstat.monitor.*package中的类 from 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分发的图书馆!
  • 你不能得到 tool.jar从Maven回购中;用Maven配置它有点棘手
  • tool.jar可能包含与平台相关的(原生?)代码,因此它不容易分派
  • 它在所有正在运行的(本地)JVM应用程序都是“可监视”的假设下运行。从Java 6看来,所有应用程序通常都是(除非您主动配置相反的)
  • 它可能仅适用于Java 6+
  • Eclipse不会发布主类,因此您不会轻易获得Eclipse PID。MonitoredVmUtil中的错误?

更新:我刚刚仔细检查过JPS是否使用这种方式,即Jvmstat库(tool.jar的一部分)。因此,无需像外部示例一样将JPS称为外部进程,而直接调用Jvmstat库。您可以通过这种方式在本地运行的所有JVM列表。参见JPS 源代码


如果mainClass.getName()不起作用,请尝试使用mainClass.getCanonicalName();。
Narendra Parmar

3

我将此添加到其他解决方案中。

使用Java 10获得 process id

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




1

我发现了一个可能有点过时的解决方案,并且没有在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();
}

-1

这是我的解决方案:

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
c16年

什么是正确的解决方案,以便我可以更新代码?
PartialData '16

-6

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

netstat -tupln | grep portNumber

3
如果您正在调用Shell脚本,则不妨做一些更简单的操作,bash -c 'echo $PPID'或者执行上面
Rich
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.