Wie kann ein Java-Programm eine eigene Prozess-ID erhalten?

Antworten:

322

Es gibt keine plattformunabhängige Methode, die garantiert in allen JVM-Implementierungen funktioniert. ManagementFactory.getRuntimeMXBean().getName()sieht aus wie die beste (nächstgelegene) Lösung. Es ist kurz und funktioniert wahrscheinlich in jeder weit verbreiteten Implementierung.

Unter Linux + Windows wird ein Wert wie 12345@hostname( 12345als Prozess-ID) zurückgegeben. Beachten Sie jedoch, dass laut Dokument keine Garantie für diesen Wert besteht:

Gibt den Namen der laufenden Java Virtual Machine zurück. Die zurückgegebene Namenszeichenfolge kann eine beliebige Zeichenfolge sein, und eine Java Virtual Machine-Implementierung kann plattformspezifische nützliche Informationen in die zurückgegebene Namenszeichenfolge einbetten. Jede laufende virtuelle Maschine kann einen anderen Namen haben.

In Java 9 kann die neue Prozess-API verwendet werden:

long pid = ProcessHandle.current().pid();
Wouter Coekaerts
quelle
2
Diese Lösung ist wirklich zerbrechlich. Unten finden Sie eine Antwort zu Hyperic Sigar.
Michael Klishin
Diese PID
Aquarius Power
111

Sie könnten JNA verwenden . Leider gibt es noch keine gemeinsame JNA-API, um die aktuelle Prozess-ID abzurufen, aber jede Plattform ist ziemlich einfach:

Windows

Stellen Sie sicher, dass Sie jna-platform.jardann haben:

int pid = Kernel32.INSTANCE.GetCurrentProcessId();

Unix

Erklären:

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

Dann:

int pid = CLibrary.INSTANCE.getpid();

Java 9

Unter Java 9 kann die neue Prozess-API verwendet werden, um die aktuelle Prozess-ID abzurufen. Zuerst greifen Sie zu einem Handle für den aktuellen Prozess und fragen dann die PID ab:

long pid = ProcessHandle.current().pid();
Luke Quinane
quelle
4
+1 Aber ich befürchte, dass es in einer Umgebung mit eingeschränkter Sicherheit nicht funktionieren sollte (Tomcat, WebLogic usw.).
ATorras
Die getpid()Lösung funktioniert auch für OS X mit einem JNA-Aufruf der Bibliothek "System".
Daniel Widdis
Allerdings bekomme ich error: cannot find symbolfür jdk9
skytree
56

Hier ist eine Backdoor-Methode, die möglicherweise nicht mit allen VMs funktioniert, aber sowohl unter Linux als auch unter Windows funktionieren sollte ( ursprüngliches Beispiel hier ):

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);
Brad Mace
quelle
2
Sehr schön, nur überprüft, dass es sowohl auf dem JRockit als auch auf den Hotspot-JVMs funktioniert.
Drupad Panchal
1
Schöne Problemumgehung. Ich gehe davon aus, dass es einen guten Grund gibt, warum diese Methode (und andere in der Klasse) nicht öffentlich und leicht zugänglich sind, und ich bin gespannt, was es ist.
Fragorl
2
Das Oracle Java-Team hat angekündigt, alle Nicht-Java-Pakete (z. B. sun. *) Zu verbergen. Ich glaube, ab Java 10 (geplant für 2018 - möglicherweise Java 9). Wenn Sie eine ähnliche Implementierung wie oben haben, möchten Sie möglicherweise eine Alternative finden, damit Ihr Code nicht beschädigt wird.
Hfontanez
Funktioniert bei mir auch nicht als Sun. * -Pakete werden wahrscheinlich entfernt oder sind nicht zugänglich (VMManagement kann nicht in einen Typ aufgelöst werden).
Mike Stoddart
Aufgrund der Funktionsweise der Reflexion ist kein Zugriff auf sun.management. * Erforderlich. Führen Sie einfach das getDeclaredMethodfür das Objekt aus, das von zurückgegeben wird jvm.get(runtime).
Andrew T Finnell
36

Versuchen Sie es mit Sigar . sehr umfangreiche APIs. Apache 2 Lizenz.

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();
}
Ashwin Jayaprakash
quelle
6
Sie hätten viel mehr positive Stimmen, wenn Sie erklären würden, wie man SIGAR dafür verwendet
Brad Mace
1
Link ist unterbrochen. Sie geben ein Beispiel für die Verwendung, aber gibt es eine Maven-Abhängigkeit dafür?
Jules
github.com/hyperic/sigar scheint ein nutzbarer Ort zu sein; Dies zeigt auch, dass es sich um nativen Code mit Java-Bindungen handelt, was ihn für viele Kontexte möglicherweise weniger zu einer Lösung macht.
Zastai
26

Die folgende Methode versucht, die PID zu extrahieren 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;
}

Rufen Sie getProcessId("<PID>")zum Beispiel einfach an .

Martin
quelle
6
VisualVM verwendet ähnlichen Code, um die PID selbst abzurufen. Überprüfen Sie com.sun.tools.visualvm.application.jvm.Jvm # ApplicationSupport # createCurrentApplication (). Sie sind die Experten, daher sieht es nach einer zuverlässigen plattformübergreifenden Lösung aus.
Espinosa
@Espinosa VisualVM unterstützt nur die Ausführung auf einer OpenJDK / Oracle / GraalVM-JVM (ab 2020). Dies bedeutet, dass sie entsprechende Annahmen treffen können. Wenn Sie dasselbe tun, werden Sie herstellerabhängig und Ihr Code kann beschädigt werden, wenn er beispielsweise auf J9 ausgeführt wird.
Thorbjørn Ravn Andersen
24

Für ältere JVM unter 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";
}
Emmanuel Bourg
quelle
52
Wenn du das machst, dann mach es einfach int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());. Warum die zusätzlichen Drehungen?
David Citron
2
Für die Neugierigen funktioniert der Trick in @Davids Kommentar tatsächlich. Kann jemand sagen warum? Eine Art Magie in der getCanonicalFile()Methode, die "Selbst" in die PID umwandelt?
GreenGiant
7
getCanonicalFile () löst den Symlink / proc / self / -> / proc / 1234 auf, versuche ls -al / proc / self
Michael
2
@ DavidCitron +1! Sie haben Recht, ich habe nicht in getCanonicalFile gedacht :-)
ggrandes
18

Sie können mein Projekt überprüfen: JavaSysMon auf GitHub. Es bietet eine Prozess-ID und eine Reihe anderer Dinge (CPU-Auslastung, Speichernutzung) plattformübergreifend (derzeit Windows, Mac OSX, Linux und Solaris).

Jez Humble
quelle
Ist es möglich, die PID eines Prozesses von Java zu starten? Oder starten Sie einen Prozess "auf Ihre Weise", um dieses Problem zu beheben?
Panayotis
17

Seit Java 9 gibt es eine Methode Process.getPid (), die die native ID eines Prozesses zurückgibt:

public abstract class Process {

    ...

    public long getPid();
}

Um die Prozess-ID des aktuellen Java-Prozesses abzurufen, können Sie die folgende ProcessHandleSchnittstelle verwenden:

System.out.println(ProcessHandle.current().pid());
ZhekaKozlov
quelle
2
Können Sie erklären, wie Sie eine Prozessinstanz für den aktuell ausgeführten Prozess in Java 9 erhalten?
Augusto
Tolle Antwort für die Verwendung von Java 9 im Jahr 2016! Können Sie einen Link zu den Javadocs hinzufügen? danke
crazyGuy
8

In Scala:

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

Dies sollte Ihnen eine Problemumgehung auf Unix-Systemen bieten, bis Java 9 veröffentlicht wird. (Ich weiß, die Frage betraf Java, aber da es für Scala keine gleichwertige Frage gibt, wollte ich diese für Scala-Benutzer belassen, die möglicherweise auf dieselbe Frage stoßen.)

Florin T.
quelle
7

Der Vollständigkeit halber gibt es in Spring Boot einen Wrapper für die

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

Lösung. Wenn eine Ganzzahl erforderlich ist, kann dies zu einem Einzeiler zusammengefasst werden:

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

Wenn jemand Spring Boot bereits verwendet, verwendet er möglicherweise org.springframework.boot.ApplicationPid

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

Die toString () -Methode gibt die PID oder '???' aus.

Vorsichtsmaßnahmen bei der Verwendung der ManagementFactory werden bereits in anderen Antworten erläutert.

l00tr
quelle
7
java.lang.management.ManagementFactory.getRuntimeMXBean().getName().split("@")[0]
Markus
quelle
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;
}
Jaskey
quelle
5

Das Neueste, was ich gefunden habe, ist, dass es eine Systemeigenschaft namens sun.java.launcher.pidgibt, die zumindest unter Linux verfügbar ist. Mein Plan ist es, das zu verwenden und wenn es nicht gefunden wird, das zu verwenden JMX bean.

Jared
quelle
In meinem Fall mit Ubuntu 16.04 und Java 8 gibt es null
david.perez
4

Es hängt davon ab, wo Sie nach Informationen suchen.

Wenn Sie über die Konsole nach Informationen suchen, können Sie den Befehl jps verwenden. Der Befehl gibt eine Ausgabe ähnlich dem Unix ps-Befehl aus und wird mit dem JDK geliefert, da ich glaube, dass 1.5

Wenn Sie aus dem Prozess schauen, ist die RuntimeMXBean (wie von Wouter Coekaerts gesagt) wahrscheinlich Ihre beste Wahl. Die Ausgabe von getName () unter Windows mit Sun JDK 1.6 u7 hat die Form [PROCESS_ID] @ [MACHINE_NAME]. Sie könnten jedoch versuchen, jps auszuführen und das Ergebnis daraus zu analysieren:

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

Bei Ausführung ohne Optionen sollte die Ausgabe die Prozess-ID gefolgt vom Namen sein.

Ryan P.
quelle
1
Das JPS-Tool verwendet die jvmstat-Bibliothek, die Teil von tools.jar ist. Überprüfen Sie mein Beispiel oder sehen Sie den JPS-Quellcode: grepcode.com/file_/repository.grepcode.com/java/root/jdk/… . Es ist nicht erforderlich, JPS als externen Prozess aufzurufen. Verwenden Sie die jvmstat-Bibliothek direkt.
Espinosa
4

Dies ist der Code, den JConsole und möglicherweise von jps und VisualVM verwenden. Es verwendet Klassen aus sun.jvmstat.monitor.*Paket, aus 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;
    }
}

Es gibt nur wenige Fänge:

  • Das tool.jarist eine Bibliothek, die mit Oracle JDK vertrieben wird, aber nicht mit JRE!
  • Sie können nicht tool.jarvon Maven Repo bekommen; Die Konfiguration mit Maven ist etwas schwierig
  • Der tool.jarenthält wahrscheinlich plattformabhängigen (nativen?) Code, sodass er nicht einfach zu verteilen ist
  • Es wird davon ausgegangen, dass alle (lokal) laufenden JVM-Apps "überwachbar" sind. Es sieht so aus, als ob ab Java 6 im Allgemeinen alle Apps vorhanden sind (es sei denn, Sie konfigurieren aktiv das Gegenteil).
  • Es funktioniert wahrscheinlich nur für Java 6+
  • Eclipse veröffentlicht keine Hauptklasse, so dass Sie Eclipse PID nicht leicht erhalten können. Fehler in MonitoredVmUtil?

UPDATE: Ich habe gerade überprüft, ob JPS diese Methode verwendet, dh die Jvmstat-Bibliothek (Teil von tool.jar). Es ist also nicht erforderlich, JPS als externen Prozess aufzurufen. Rufen Sie die Jvmstat-Bibliothek direkt auf, wie in meinem Beispiel gezeigt. Auf diese Weise können Sie auch eine Liste aller JVMs abrufen, die auf localhost ausgeführt werden. Siehe JPS- Quellcode :

Espinosa
quelle
Wenn mainClass.getName () nicht funktioniert, versuchen Sie, mainClass.getCanonicalName () zu verwenden.
Narendra Parmar
3

Ich füge dies anderen Lösungen hinzu.

mit Java 10 zu bekommen process id

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

Sie können es getpid()in JNR-Posix versuchen .

Es hat einen Windows POSIX-Wrapper, der getpid () von libc aus aufruft.

Kervin
quelle
1

Ich weiß, dass dies ein alter Thread ist, aber ich wollte darauf hinweisen, dass die API zum Abrufen der PID (sowie anderer Manipulationen des Java-Prozesses zur Laufzeit) der Process-Klasse in JDK 9 hinzugefügt wird: http: // openjdk. java.net/jeps/102

Brandon Heck
quelle
9
Wow, das war schnell. Es dauerte nur etwa sieben Jahre! 😃
Dmitry Shechtman
1

Ich habe eine Lösung gefunden, die vielleicht ein Randfall ist, und ich habe sie nicht auf einem anderen Betriebssystem als Windows 10 ausprobiert, aber ich denke, es lohnt sich, sie zu bemerken.

Wenn Sie mit J2V8 und nodejs arbeiten, können Sie eine einfache Javascript-Funktion ausführen, die Ihnen die PID des Java-Prozesses zurückgibt.

Hier ist ein Beispiel:

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

Hier ist meine Lösung:

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;
    }
PartialData
quelle
Dies prüft nicht, ob pid verwendet wird, aber ob pid gleich dem aktuellen Prozess pid
c0der
Was ist die richtige Lösung, damit ich meinen Code aktualisieren kann?
PartialData
-6

Dies ist, was ich verwendet habe, als ich ähnliche Anforderungen hatte. Dies bestimmt die PID des Java-Prozesses korrekt. Lassen Sie Ihren Java-Code einen Server auf einer vordefinierten Portnummer erzeugen und führen Sie dann Betriebssystembefehle aus, um die PID herauszufinden, die den Port überwacht. Für Linux

netstat -tupln | grep portNumber
Subhash
quelle
3
Wenn Sie Shell-Skripte aufrufen, können Sie auch etwas Einfacheres tun, bash -c 'echo $PPID'oder die / proc- Antworten oben
Rich