W jaki sposób program Java może uzyskać własny identyfikator procesu?

Odpowiedzi:

322

Nie istnieje sposób niezależny od platformy, który gwarantowałby działanie we wszystkich implementacjach Jvm. ManagementFactory.getRuntimeMXBean().getName()wygląda jak najlepsze (najbliższe) rozwiązanie. Jest krótki i prawdopodobnie działa w każdej implementacji w szerokim użyciu.

W systemie Linux + Windows zwraca wartość typu 12345@hostname( 12345będącą identyfikatorem procesu). Uważaj jednak, że zgodnie z dokumentami nie ma żadnych gwarancji dotyczących tej wartości:

Zwraca nazwę reprezentującą działającą maszynę wirtualną Java. Zwrócony ciąg nazwy może być dowolnym dowolnym ciągiem, a implementacja wirtualnej maszyny Java może osadzić przydatne informacje specyficzne dla platformy w zwróconym ciągu nazwy. Każda działająca maszyna wirtualna może mieć inną nazwę.

W Javie 9 można użyć nowego interfejsu API procesu :

long pid = ProcessHandle.current().pid();
Wouter Coekaerts
źródło
2
To rozwiązanie jest naprawdę kruche. Zobacz odpowiedź na temat Hyperic Sigar poniżej.
Michael Klishin
ten pid dobrze jest zapisać w pliku blokady jako stackoverflow.com/a/9020391/1422630
Aquarius Power
111

Możesz użyć JNA . Niestety nie ma jeszcze wspólnego interfejsu API JNA, aby uzyskać bieżący identyfikator procesu, ale każda platforma jest dość prosta:

Windows

Upewnij się, że masz jna-platform.jar:

int pid = Kernel32.INSTANCE.GetCurrentProcessId();

Unix

Ogłosić:

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

Następnie:

int pid = CLibrary.INSTANCE.getpid();

Java 9

W Javie 9 nowy interfejs API procesu może być używany do uzyskania bieżącego identyfikatora procesu. Najpierw łapiesz uchwyt do bieżącego procesu, a następnie odpytujesz PID:

long pid = ProcessHandle.current().pid();
Luke Quinane
źródło
4
+1 Ale obawiam się, że w środowisku o ograniczonym bezpieczeństwie nie powinno działać (tomcat, WebLogic itp.).
ATorras,
getpid()Rozwiązanie działa również dla OS X, z uprzednim JNA do biblioteki „System”.
Daniel Widdis
Jednak mam error: cannot find symboldla jdk9
skytree
56

Oto metoda backdoora, która może nie działać ze wszystkimi maszynami wirtualnymi, ale powinna działać zarówno na systemie Linux, jak i Windows ( oryginalny przykład tutaj ):

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
źródło
2
bardzo fajnie, właśnie zweryfikowałem, że działa zarówno na JRockit, jak i na Hotspot JVM.
Drupad Panchal,
1
Niezłe obejście. Zakładam, że istnieje dobry powód, dla którego ta metoda (i inne w klasie) nie jest publiczna i łatwo dostępna, i jestem ciekawy, co to jest.
fragorl
2
Zespół Oracle Java ogłosił, że zamierza ukryć wszystkie pakiety inne niż Java (tj. Sun. *). Wierzę, że począwszy od Java 10 (zaplanowanego na 2018 rok - może Java 9). Jeśli masz podobną implementację jak powyższa, możesz wymyślić alternatywę, aby Twój kod się nie zepsuł.
hfontanez
Nie działa dla mnie ani jako słońce. * Pakiety są prawdopodobnie usunięte lub niedostępne (VMManagement nie może być rozpoznany jako typ).
Mike Stoddart,
Ze względu na sposób działania odbicia dostęp do sun.management. * Nie jest potrzebny. Po prostu wykonaj getDeclaredMethodobiekt zwrócony przez jvm.get(runtime).
Andrew T Finnell,
36

Wypróbuj Sigar . bardzo rozbudowane interfejsy API. Licencja 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();
}
Ashwin Jayaprakash
źródło
6
Miałbyś o wiele więcej pozytywnych opinii, gdybyś wyjaśnił, jak do tego użyć SIGAR
Brad Mace
1
Link jest zepsuty. Dajesz przykład, jak z tego korzystać, ale czy jest w tym zależność od raju?
Jules
github.com/hyperic/sigar wydaje się być użyteczną lokalizacją; pokazuje to również, że jest to natywny kod z powiązaniami Java, co może sprawić, że będzie mniej rozwiązaniem dla wielu kontekstów.
Zastai
26

Poniższa metoda próbuje wyodrębnić PID z 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;
}

Na getProcessId("<PID>")przykład zadzwoń .

Jaskółka oknówka
źródło
6
VisualVM używa podobnego kodu do uzyskania własnego PID, sprawdź com.sun.tools.visualvm.application.jvm.Jvm # ApplicationSupport # createCurrentApplication (). Są ekspertami, więc wygląda na niezawodne, wieloplatformowe rozwiązanie.
Espinosa,
@Espinosa VisualVM obsługuje tylko uruchomioną JVM OpenJDK / Oracle / GraalVM (od 2020 r.). Oznacza to, że mogą odpowiednio przyjmować założenia. Jeśli zrobisz to samo, uzależnisz się od dostawcy i kod może się zepsuć, jeśli uruchomisz na przykład J9.
Thorbjørn Ravn Andersen
24

Dla starszych JVM, w systemie 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
źródło
52
Jeśli masz zamiar to zrobić, po prostu zrób int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());. Dlaczego dodatkowe wahania?
David Citron,
2
Dla ciekawskich sztuczka w komentarzu @Davida faktycznie działa. Czy ktoś może powiedzieć dlaczego? Jakaś magia w getCanonicalFile()metodzie, która przekształca „jaźń” w PID?
GreenGiant
7
getCanonicalFile () rozwiązuje dowiązanie symboliczne / proc / self / -> / proc / 1234, spróbuj ls -al / proc / self
Michael
2
@DavidCitron +1! masz rację, nie myślałem w getCanonicalFile :-)
grgrandes
18

Możesz sprawdzić mój projekt: JavaSysMon na GitHub. Zapewnia identyfikator procesu i kilka innych rzeczy (użycie procesora, użycie pamięci) na różnych platformach (obecnie Windows, Mac OSX, Linux i Solaris)

Jez Humble
źródło
Czy możliwe jest uzyskanie PID procesu uruchomionego przez Javę? Lub rozpocząć proces „po swojemu”, aby rozwiązać ten problem?
Panayotis
17

Od wersji Java 9 istnieje metoda Process.getPid (), która zwraca natywny identyfikator procesu:

public abstract class Process {

    ...

    public long getPid();
}

Aby uzyskać identyfikator bieżącego procesu Java, można użyć ProcessHandleinterfejsu:

System.out.println(ProcessHandle.current().pid());
ZhekaKozlov
źródło
2
Czy możesz wyjaśnić, jak uzyskać instancję procesu dla bieżącego procesu w Javie 9?
Augusto,
Świetna odpowiedź na używanie Java 9 w 2016 roku! Czy możesz dodać link do javadocs? dzięki
crazyGuy
8

W Scali:

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

To powinno dać ci obejście na systemach Unix do czasu wydania Java 9. (Wiem, że pytanie dotyczyło Javy, ale ponieważ nie ma równoważnego pytania dla Scali, chciałem zostawić to użytkownikom Scali, którzy mogą natknąć się na to samo pytanie).

Florin T.
źródło
7

Dla kompletności jest opakowanie w Spring Boot dla

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

rozwiązanie. Jeśli wymagana jest liczba całkowita, można to zsumować do jednej linijki:

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

Jeśli ktoś już używa Spring Boot, może użyć org.springframework.boot.ApplicationPid

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

Metoda toString () wypisuje pid lub „???”.

Ostrzeżenia dotyczące korzystania z ManagementFactory zostały już omówione w innych odpowiedziach.

100tr
źródło
7
java.lang.management.ManagementFactory.getRuntimeMXBean().getName().split("@")[0]
markus
źródło
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
źródło
5

Ostatnie stwierdziłem, że istnieje właściwość systemowa o nazwie, sun.java.launcher.pidktóra jest dostępna przynajmniej w systemie Linux. Mój plan jest taki, a jeśli nie można go użyć JMX bean.

Jared
źródło
W moim przypadku z Ubuntu 16.04 i Javą 8 zwraca wartość null
david.perez
4

To zależy od tego, skąd szukasz informacji.

Jeśli szukasz informacji z konsoli, możesz użyć polecenia jps. Polecenie daje dane wyjściowe podobne do polecenia ps w systemie Unix i jest dostarczane z JDK, ponieważ uważam, że 1.5

Jeśli szukasz z procesu, RuntimeMXBean (jak powiedział Wouter Coekaerts) jest prawdopodobnie najlepszym wyborem. Dane wyjściowe getName () w systemie Windows używającym Sun JDK 1.6 u7 mają postać [PROCESS_ID] @ [MACHINE_NAME]. Możesz jednak spróbować wykonać jps i przeanalizować wynik:

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

Jeśli uruchamiany jest bez opcji, wynikiem powinien być identyfikator procesu, a po nim nazwa.

Ryan P.
źródło
1
Narzędzie JPS wykorzystuje bibliotekę jvmstat, część narzędzia.jar. Sprawdź mój przykład lub zobacz kod źródłowy JPS: grepcode.com/file_/repository.grepcode.com/java/root/jdk/… . Nie ma potrzeby wywoływania JPS jako procesu zewnętrznego, użyj biblioteki jvmstat bezpośrednio.
Espinosa,
4

Jest to kod JConsole i potencjalnie używa jps i VisualVM. Wykorzystuje klasy z sun.jvmstat.monitor.*pakietu, z 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;
    }
}

Istnieje kilka połowów:

  • The tool.jarJest biblioteką rozprowadzany z Oracle JDK ale nie JRE!
  • Nie możesz dostać tool.jar z repozytorium Maven; skonfigurowanie go za pomocą Maven jest nieco trudne
  • The tool.jarPrawdopodobnie zawiera platform zależnej (native?) Kodu, więc nie jest łatwo dystrybuowana
  • Działa przy założeniu, że wszystkie (lokalne) działające aplikacje JVM są „monitorowalne”. Wygląda na to, że z Javy 6 ogólnie wszystkie aplikacje są (chyba że aktywnie skonfigurujesz odwrotnie)
  • Prawdopodobnie działa tylko dla Java 6+
  • Eclipse nie publikuje głównej klasy, więc nie dostaniesz łatwo Eclipse PID Błąd w MonitoredVmUtil?

AKTUALIZACJA: Właśnie dwukrotnie sprawdziłem, czy JPS używa tej metody, to znaczy biblioteki Jvmstat (część tool.jar). Nie ma więc potrzeby wywoływania JPS jako zewnętrznego procesu, wywoływania biblioteki Jvmstat bezpośrednio, jak pokazuje mój przykład. W ten sposób można również uzyskać listę wszystkich JVM uruchomionych na localhost. Zobacz kod źródłowy JPS :

Espinosa
źródło
jeśli mainClass.getName () nie działa, spróbuj użyć mainClass.getCanonicalName ();
Narendra Parmar
3

Dodam to do innych rozwiązań.

z Java 10, aby uzyskać process id

final RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
final long pid = runtime.getPid();
out.println("Process ID is '" + pid);
mrsrinivas
źródło
1

Możesz spróbować getpid()w JNR-Posix .

Ma opakowanie systemu Windows POSIX, które wywołuje getpid () z libc.

Kervin
źródło
1

Wiem, że to stary wątek, ale chciałem przywołać ten interfejs API do pobierania PID (a także innych manipulacji procesem Java w czasie wykonywania) jest dodawany do klasy Process w JDK 9: http: // openjdk. java.net/jeps/102

Brandon Heck
źródło
9
Wow, to było szybkie. Zajęło to tylko około siedmiu lat! 😃
Dmitry
1

Znalazłem rozwiązanie, które może być trochę przypadkowe i nie wypróbowałem go na innym systemie operacyjnym niż Windows 10, ale myślę, że warto to zauważyć.

Jeśli znajdziesz pracę z J2V8 i nodejs, możesz uruchomić prostą funkcję javascript zwracającą ci pid procesu java.

Oto przykład:

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
źródło
-1

Oto moje rozwiązanie:

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
źródło
Nie sprawdza to, czy pid jest w użyciu, ale czy pid jest równy bieżącemu procesowi pid
c0der
Jakie jest prawidłowe rozwiązanie, aby zaktualizować kod?
PartialData
-6

Tego właśnie użyłem, gdy miałem podobne wymagania. To poprawnie określa PID procesu Java. Pozwól, aby kod Java odrodził serwer na wstępnie zdefiniowanym numerze portu, a następnie uruchom komendę systemu operacyjnego, aby dowiedzieć się, czy PID nasłuchuje na porcie. Dla systemu Linux

netstat -tupln | grep portNumber
Subhash
źródło
3
Jeśli wywołujesz skrypty powłoki, równie dobrze możesz zrobić coś prostszego bash -c 'echo $PPID'lub powyższe odpowiedzi
Rich