Come può un programma Java ottenere il proprio ID processo?


Risposte:


322

Non esiste un modo indipendente dalla piattaforma che può essere garantito per funzionare in tutte le implementazioni jvm. ManagementFactory.getRuntimeMXBean().getName()sembra la migliore soluzione (più vicina). È breve e probabilmente funziona in tutte le implementazioni in largo uso.

Su linux + windows restituisce un valore simile 12345@hostname( 12345essendo l'id del processo). Attenzione però che secondo i documenti non ci sono garanzie su questo valore:

Restituisce il nome che rappresenta la macchina virtuale Java in esecuzione. La stringa del nome restituita può essere qualsiasi stringa arbitraria e un'implementazione della macchina virtuale Java può scegliere di incorporare informazioni utili specifiche della piattaforma nella stringa del nome restituito. Ogni macchina virtuale in esecuzione potrebbe avere un nome diverso.

In Java 9 è possibile utilizzare la nuova API di processo :

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

2
Questa soluzione è davvero fragile. Vedi una risposta su Hyperic Sigar di seguito.
Michael Klishin,

quel pid è buono per scrivere su un file di blocco come stackoverflow.com/a/9020391/1422630
Aquarius Power

111

Potresti usare JNA . Sfortunatamente non esiste ancora un'API JNA comune per ottenere l'ID processo corrente, ma ogni piattaforma è piuttosto semplice:

finestre

Assicurati di avere jna-platform.jarquindi:

int pid = Kernel32.INSTANCE.GetCurrentProcessId();

Unix

Dichiarare:

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

Poi:

int pid = CLibrary.INSTANCE.getpid();

Java 9

In Java 9 è possibile utilizzare la nuova API di processo per ottenere l'ID processo corrente. Innanzitutto prendi un handle per il processo corrente, quindi esegui una query sul PID:

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

4
+1 Ma temo che in un ambiente con problemi di sicurezza non dovrebbe funzionare (tomcat, WebLogic, ecc.).
ATorras,

La getpid()soluzione funziona anche per OS X, con una chiamata JNA alla libreria "Sistema".
Daniel Widdis,

Tuttavia, ottengo error: cannot find symbolper jdk9
skytree il

56

Ecco un metodo backdoor che potrebbe non funzionare con tutte le macchine virtuali ma dovrebbe funzionare sia su Linux che su Windows ( esempio originale qui ):

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
molto bello, ho appena verificato che funziona sia su JRockit che su JVM Hotspot.
Drupad Panchal,

1
Bella soluzione alternativa. Suppongo che ci sia una buona ragione per cui questo metodo (e altri nella classe) non sono pubblici e facilmente accessibili, e sono curioso di sapere di cosa si tratta.
Fragorl

2
Il team Oracle Java ha annunciato l'intenzione di nascondere tutti i pacchetti non java (ovvero sun. *) Credo che inizierò con Java 10 (previsto per il 2018 - forse Java 9). Se hai un'implementazione simile a quella sopra, potresti voler escogitare un'alternativa in modo che il tuo codice non si rompa.
hfontanez,

Non funziona neanche per me come i pacchetti sun. * Sono probabilmente rimossi o inaccessibili (VMManagement non può essere risolto in un tipo).
Mike Stoddart,

A causa del modo in cui funziona la riflessione, l'accesso a sun.management. * Non è necessario. Esegui semplicemente getDeclaredMethodl'oggetto restituito da jvm.get(runtime).
Andrew T Finnell,

36

Prova Sigar . API molto estese. Licenza 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
Avresti molti più voti se spiegassi come usare SIGAR per questo
Brad Mace,

1
Il collegamento è interrotto. Fai un esempio di come usarlo, ma esiste una dipendenza maven?
Jules,

github.com/hyperic/sigar sembra essere una posizione utilizzabile; questo dimostra anche che si tratta di codice nativo con collegamenti Java, che potrebbe renderlo meno una soluzione per molti contesti.
Zastai,

26

Il seguente metodo tenta di estrarre il PID da 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;
}

Basta chiamare getProcessId("<PID>"), per esempio.


6
VisualVM utilizza un codice simile per ottenere il PID automatico, controlla com.sun.tools.visualvm.application.jvm.Jvm # ApplicationSupport # createCurrentApplication (). Sono gli esperti, quindi sembra una soluzione affidabile e multipiattaforma.
Espinosa,

@Espinosa VisualVM supporta solo l'esecuzione su una JVM OpenJDK / Oracle / GraalVM (dal 2020). Ciò significa che possono fare ipotesi di conseguenza. Se fai lo stesso, diventi dipendente dal fornitore e il tuo codice potrebbe rompersi se eseguito ad esempio J9.
Thorbjørn Ravn Andersen,

24

Per JVM precedente, in 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
Se hai intenzione di farlo, allora fallo int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());. Perché le giravolte extra?
David Citron,

2
Per i curiosi, il trucco nel commento di @David funziona davvero. Qualcuno può dire perché? Una sorta di magia nel getCanonicalFile()metodo che converte "sé" in PID?
GreenGiant

7
getCanonicalFile () risolve il collegamento simbolico / proc / self / -> / proc / 1234, prova ls -al / proc / self
Michael

2
@DavidCitron +1! hai ragione, non pensavo in getCanonicalFile :-)
ggrandes

18

Puoi dare un'occhiata al mio progetto: JavaSysMon su GitHub. Fornisce ID di processo e un sacco di altre cose (utilizzo della CPU, utilizzo della memoria) multipiattaforma (attualmente Windows, Mac OSX, Linux e Solaris)


È possibile ottenere il PID di un processo avviato da Java? O avviare un processo "a modo tuo" per risolvere questo problema?
Panayotis,

17

Da Java 9 esiste un metodo Process.getPid () che restituisce l'ID nativo di un processo:

public abstract class Process {

    ...

    public long getPid();
}

Per ottenere l'ID di processo del processo Java corrente, è possibile utilizzare l' ProcessHandleinterfaccia:

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

2
Puoi spiegare come ottenere un'istanza di processo per l'attuale processo in esecuzione in Java 9?
Augusto,

Ottima risposta per l'utilizzo di Java 9 nel 2016! Puoi aggiungere un link al javadocs? grazie
crazyGuy

8

Alla Scala:

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

Questo dovrebbe darti una soluzione alternativa sui sistemi Unix fino al rilascio di Java 9. (Lo so, la domanda riguardava Java, ma poiché non esiste una domanda equivalente per Scala, volevo lasciarla agli utenti Scala che potrebbero inciampare nella stessa domanda.)


7

Per completezza c'è un wrapper in Spring Boot per il

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

soluzione. Se è richiesto un numero intero, questo può essere riassunto in una riga:

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

Se qualcuno utilizza già l'avvio di Spring, potrebbe usare org.springframework.boot.ApplicationPid

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

Il metodo toString () stampa il pid o '???'.

Gli avvertimenti che utilizzano ManagementFactory sono già discussi in altre risposte.


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

L'ultima che ho trovato è che esiste una proprietà di sistema chiamata sun.java.launcher.pidche è disponibile almeno su Linux. Il mio piano è quello di usarlo e se non si trova ad usare il JMX bean.


Nel mio caso con Ubuntu 16.04 e Java 8, restituisce null
david.perez

4

Dipende da dove stai cercando le informazioni.

Se stai cercando le informazioni dalla console puoi usare il comando jps. Il comando fornisce un output simile al comando ps Unix e viene fornito con il JDK poiché credo 1.5

Se stai osservando il processo, RuntimeMXBean (come detto da Wouter Coekaerts) è probabilmente la scelta migliore. L'output di getName () su Windows utilizzando Sun JDK 1.6 u7 è nel formato [PROCESS_ID] @ [MACHINE_NAME]. Potresti comunque provare a eseguire jps e analizzare il risultato da quello:

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

Se eseguito senza opzioni, l'output dovrebbe essere l'id del processo seguito dal nome.


1
Lo strumento JPS utilizza la libreria jvmstat, parte di tools.jar. Controlla il mio esempio o vedi il codice sorgente JPS: grepcode.com/file_/repository.grepcode.com/java/root/jdk/… . Non è necessario chiamare JPS come processo esterno, utilizzare direttamente la libreria jvmstat.
Espinosa,

4

Questo è il codice JConsole e potenzialmente utilizza jps e VisualVM. Utilizza le classi dal sun.jvmstat.monitor.*pacchetto, da 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;
    }
}

Ci sono alcune catture:

  • Il tool.jar è una libreria distribuita con Oracle JDK ma non JRE!
  • Non puoi ottenere tool.jar dal repository Maven; configurarlo con Maven è un po 'complicato
  • Il tool.jar probabilmente contiene codice dipendente dalla piattaforma (nativo?) Quindi non è facilmente distribuibile
  • Suppone che tutte le app JVM (locali) in esecuzione siano "monitorabili". Sembra che da Java 6 tutte le app siano generalmente (a meno che non si configuri attivamente il contrario)
  • Probabilmente funziona solo per Java 6+
  • Eclipse non pubblica la classe principale, quindi non otterrai facilmente Eclipse PID Bug in MonitoredVmUtil?

AGGIORNAMENTO: Ho appena verificato che JPS usi in questo modo, ovvero la libreria Jvmstat (parte di tool.jar). Quindi non è necessario chiamare JPS come processo esterno, chiamare direttamente la libreria Jvmstat come mostra il mio esempio. Puoi anche ottenere l'elenco di tutte le JVM che girano su localhost in questo modo. Vedi codice sorgente JPS :


se mainClass.getName () non funziona provare a utilizzare mainClass.getCanonicalName ();
Narendra Parmar,

3

Sto aggiungendo questo, ad altre soluzioni.

con Java 10, per ottenere process id

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


1

Puoi provare getpid()in JNR-Posix .

Ha un wrapper POSIX per Windows che chiama getpid () fuori da libc.



1

Ho trovato una soluzione che potrebbe essere un po 'un caso limite e non l'ho provata su un sistema operativo diverso da Windows 10, ma penso che valga la pena notare.

Se ti trovi a lavorare con J2V8 e nodejs, puoi eseguire una semplice funzione javascript che ti restituisce il pid del processo java.

Ecco un esempio:

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

Ecco la mia soluzione:

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

Questo non controlla se pid è in uso, ma se pid è uguale al processo corrente pid
c0der

Qual è la soluzione corretta per poter aggiornare il mio codice?
PartialData

-6

Questo è quello che ho usato quando avevo requisiti simili. Ciò determina correttamente il PID del processo Java. Lascia che il tuo codice java generi un server su un numero di porta predefinito e quindi esegui i comandi del sistema operativo per scoprire l'ascolto PID sulla porta. Per Linux

netstat -tupln | grep portNumber

3
Se stai chiamando script di shell, potresti anche fare qualcosa di più semplice bash -c 'echo $PPID'o le risposte
Rich
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.