¿Cómo puede un programa Java obtener su propia ID de proceso?

Respuestas:

322

No existe una forma independiente de la plataforma que pueda garantizarse que funcione en todas las implementaciones de jvm. ManagementFactory.getRuntimeMXBean().getName()Parece la mejor solución (más cercana). Es breve, y probablemente funciona en todas las implementaciones en uso amplio.

En linux + windows devuelve un valor como 12345@hostname( 12345siendo el id del proceso). Sin embargo, tenga en cuenta que según los documentos , no hay garantías sobre este valor:

Devuelve el nombre que representa la máquina virtual Java en ejecución. La cadena de nombre devuelta puede ser cualquier cadena arbitraria y una implementación de máquina virtual Java puede elegir incrustar información útil específica de la plataforma en la cadena de nombre devuelta. Cada máquina virtual en ejecución podría tener un nombre diferente.

En Java 9 , se puede utilizar la nueva API de proceso :

long pid = ProcessHandle.current().pid();
Wouter Coekaerts
fuente
2
Esta solución es realmente frágil. Vea una respuesta sobre Hyperic Sigar a continuación.
Michael Klishin
que pid es bueno escribir en un archivo de bloqueo como stackoverflow.com/a/9020391/1422630
Aquarius Power
111

Podrías usar JNA . Desafortunadamente, todavía no hay una API JNA común para obtener la ID de proceso actual, pero cada plataforma es bastante simple:

Ventanas

Asegúrate de tener jna-platform.jarentonces:

int pid = Kernel32.INSTANCE.GetCurrentProcessId();

Unix

Declarar:

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

Entonces:

int pid = CLibrary.INSTANCE.getpid();

Java 9

En Java 9, la nueva API de proceso se puede utilizar para obtener la ID del proceso actual. Primero, toma un identificador del proceso actual, luego consulta el PID:

long pid = ProcessHandle.current().pid();
Luke Quinane
fuente
44
+1 Pero me temo que en un entorno con restricciones de seguridad no debería funcionar (tomcat, WebLogic, etc.).
ATorras
La getpid()solución también funciona para OS X, con una llamada JNA a la biblioteca "Sistema".
Daniel Widdis
Sin embargo, me sale error: cannot find symbolpara jdk9
skytree
56

Aquí hay un método de puerta trasera que podría no funcionar con todas las máquinas virtuales, pero debería funcionar tanto en Linux como en Windows ( ejemplo original aquí ):

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
fuente
2
muy bueno, solo verifiqué que funciona tanto en JRockit como en JVM de Hotspot.
Drupad Panchal
1
Buena solución. Asumiré que hay una buena razón por la cual este método (y otros en la clase) no son públicos y de fácil acceso, y tengo curiosidad por saber de qué se trata.
fragorl
2
El equipo de Oracle Java ha anunciado que tienen la intención de ocultar todos los paquetes que no sean Java (es decir, sun. *). Creo que comenzará con Java 10 (programado para 2018, tal vez Java 9). Si tiene una implementación similar a la anterior, es posible que desee encontrar una alternativa para que su código no se rompa.
hfontanez
Tampoco funciona para mí, ya que los paquetes sun. * Probablemente se eliminan o son inaccesibles (VMManagement no se puede resolver a un tipo).
Mike Stoddart el
Debido a la forma en que funciona la reflexión, no se necesita acceso a sun.management. *. Simplemente realice el getDeclaredMethoden el Objeto devuelto por jvm.get(runtime).
Andrew T Finnell
36

Prueba con Sigar . API muy extensas. Apache 2 licencia.

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
fuente
66
Tendría muchos más votos a favor si explicara cómo usar SIGAR para esto
Brad Mace
1
El enlace está roto. Da un ejemplo de cómo usar esto, pero ¿hay alguna dependencia de Maven para ello?
Jules
github.com/hyperic/sigar parece ser una ubicación utilizable; Esto también muestra que es un código nativo con enlaces Java, lo que puede hacer que sea menos una solución para muchos contextos.
Zastai
26

El siguiente método intenta extraer el PID de 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;
}

Solo llame getProcessId("<PID>"), por ejemplo.

Martín
fuente
66
VisualVM usa un código similar para obtener el PID propio, verifique com.sun.tools.visualvm.application.jvm.Jvm # ApplicationSupport # createCurrentApplication (). Ellos son los expertos, por lo que parece una solución confiable y multiplataforma.
Espinosa
@Espinosa VisualVM solo admite la ejecución en OpenJDK / Oracle / GraalVM JVM (a partir de 2020). Esto significa que pueden hacer suposiciones en consecuencia. Si hace lo mismo, dependerá del proveedor y su código puede romperse si se ejecuta, por ejemplo, en J9.
Thorbjørn Ravn Andersen
24

Para JVM anteriores, en 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
fuente
52
Si vas a hacer eso, entonces solo hazlo int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());. ¿Por qué los giros extra?
David Citron
2
Para los curiosos, el truco en el comentario de @David realmente funciona. ¿Alguien puede decir por qué? ¿Algún tipo de magia en el getCanonicalFile()método que convierte "self" al PID?
GreenGiant
77
getCanonicalFile () resuelve el enlace simbólico / proc / self / -> / proc / 1234, prueba ls -al / proc / self
Michael
2
@DavidCitron +1! tienes razón, no pensé en getCanonicalFile :-)
ggrandes
18

Puedes ver mi proyecto: JavaSysMon en GitHub. Proporciona id de proceso y un montón de otras cosas (uso de CPU, uso de memoria) multiplataforma (actualmente Windows, Mac OSX, Linux y Solaris)

Jez Humble
fuente
¿Es posible obtener el PID de un proceso iniciado por Java? ¿O iniciar un proceso "a su manera" para solucionar este problema?
Panayotis
17

Desde Java 9 hay un método Process.getPid () que devuelve la ID nativa de un proceso:

public abstract class Process {

    ...

    public long getPid();
}

Para obtener la ID del proceso del proceso Java actual, se puede usar la ProcessHandleinterfaz:

System.out.println(ProcessHandle.current().pid());
ZhekaKozlov
fuente
2
¿Puede explicar cómo obtener una instancia de Proceso para el proceso actual en ejecución en Java 9?
Augusto
¡Gran respuesta para usar Java 9 en 2016! ¿Puedes agregar un enlace a los javadocs? gracias
crazyGuy
8

En Scala:

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

Esto debería darle una solución alternativa en los sistemas Unix hasta que se lance Java 9. (Lo sé, la pregunta era sobre Java, pero como no hay una pregunta equivalente para Scala, quería dejar esto para los usuarios de Scala que podrían tropezar con la misma pregunta).

Florin T.
fuente
7

Para completar, hay un contenedor en Spring Boot para

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

solución. Si se requiere un número entero, esto se puede resumir en una línea:

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

Si alguien ya usa Spring boot, él / ella puede usar org.springframework.boot.ApplicationPid

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

El método toString () imprime el pid o '???'.

Las advertencias que usan ManagementFactory ya se analizan en otras respuestas.

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

Lo último que he encontrado es que hay una propiedad del sistema llamada sun.java.launcher.pidque está disponible al menos en Linux. Mi plan es usar eso y, si no se encuentra, usar el JMX bean.

Jared
fuente
En mi caso con Ubuntu 16.04 y Java 8, devuelve nulo
david.perez
4

Depende de dónde esté buscando la información.

Si está buscando la información de la consola, puede usar el comando jps. El comando da una salida similar al comando Unix ps y viene con el JDK ya que creo que 1.5

Si está buscando en el proceso, RuntimeMXBean (como dijo Wouter Coekaerts) es probablemente su mejor opción. El resultado de getName () en Windows con Sun JDK 1.6 u7 tiene el formato [PROCESS_ID] @ [MACHINE_NAME]. Sin embargo, podría intentar ejecutar jps y analizar el resultado a partir de eso:

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

Si se ejecuta sin opciones, la salida debería ser la identificación del proceso seguida del nombre.

Ryan P
fuente
1
La herramienta JPS utiliza la biblioteca jvmstat, parte de tools.jar. Consulte mi ejemplo o vea el código fuente JPS: grepcode.com/file_/repository.grepcode.com/java/root/jdk/… . No es necesario llamar a JPS como proceso externo, use la biblioteca jvmstat directamente.
Espinosa
4

Este es el código JConsole, y potencialmente utiliza jps y VisualVM. Utiliza clases de sun.jvmstat.monitor.*paquete, de 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;
    }
}

Hay pocas capturas:

  • los tool.jar Es una biblioteca distribuida con Oracle JDK pero no con JRE!
  • No puedes conseguir tool.jar del repositorio de Maven; configurarlo con Maven es un poco complicado
  • los tool.jar probablemente contiene código dependiente de la plataforma (nativo?) Por lo que no es fácilmente distribuibles
  • Se ejecuta bajo el supuesto de que todas las aplicaciones JVM (locales) que se ejecutan son "monitoreables". Parece que desde Java 6 todas las aplicaciones generalmente lo son (a menos que configure activamente lo contrario)
  • Probablemente solo funcione para Java 6+
  • Eclipse no publica la clase principal, por lo que no obtendrá Eclipse PID fácilmente ¿Error en MonitoredVmUtil?

ACTUALIZACIÓN: Acabo de comprobar que JPS usa de esta manera, es decir, la biblioteca Jvmstat (parte de tool.jar). Por lo tanto, no es necesario llamar a JPS como proceso externo, llame a la biblioteca Jvmstat directamente como lo muestra mi ejemplo. También puede obtener la lista de todas las JVM que se ejecutan en localhost de esta manera. Ver código fuente JPS :

Espinosa
fuente
si mainClass.getName () no funciona, intente utilizar mainClass.getCanonicalName ();
Narendra Parmar
3

Estoy agregando esto, a otras soluciones.

con Java 10, para obtener process id

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

Puedes probar getpid()en JNR-Posix .

Tiene un contenedor POSIX de Windows que invoca getpid () fuera de libc.

Kervin
fuente
1

Sé que este es un hilo antiguo, pero quería llamar a esa API para obtener el PID (así como otra manipulación del proceso Java en tiempo de ejecución) que se agrega a la clase Process en JDK 9: http: // openjdk. java.net/jeps/102

Brandon Heck
fuente
99
Wow, eso fue rápido. ¡Solo tomó unos siete años! 😃
Dmitry Shechtman
1

Encontré una solución que puede ser un poco marginal y no la probé en otro sistema operativo que no sea Windows 10, pero creo que vale la pena notarlo.

Si se encuentra trabajando con J2V8 y nodejs, puede ejecutar una función javascript simple que le devolverá el pid del proceso java.

Aquí hay un ejemplo:

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
fuente
-1

Aquí está mi solución:

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
fuente
Esto no verifica si pid está en uso, pero si pid es igual al proceso actual pid
c0der
¿Cuál es la solución correcta para poder actualizar mi código?
PartialData
-6

Esto es lo que usé cuando tenía un requisito similar. Esto determina el PID del proceso Java correctamente. Deje que su código Java genere un servidor en un número de puerto predefinido y luego ejecute comandos del sistema operativo para descubrir el PID que escucha en el puerto. Para Linux

netstat -tupln | grep portNumber
Subhash
fuente
3
Si está llamando scripts de shell, también podría hacer algo más simple bash -c 'echo $PPID'o las respuestas
Rich