Como um programa Java pode obter seu próprio ID de processo?

Respostas:

322

Não existe uma maneira independente de plataforma que possa garantir que funcione em todas as implementações da jvm. ManagementFactory.getRuntimeMXBean().getName()parece a melhor solução (mais próxima). É curto e provavelmente funciona em todas as implementações de uso amplo.

No linux + windows, ele retorna um valor como 12345@hostname( 12345sendo o ID do processo). Cuidado, porém, de acordo com os documentos , não há garantias sobre esse valor:

Retorna o nome que representa a Java virtual machine em execução. A cadeia de nome retornada pode ser qualquer cadeia arbitrária e uma implementação da máquina virtual Java pode optar por incorporar informações úteis específicas da plataforma na cadeia de nome retornada. Cada máquina virtual em execução pode ter um nome diferente.

No Java 9, a nova API de processo pode ser usada:

long pid = ProcessHandle.current().pid();
Wouter Coekaerts
fonte
2
Esta solução é realmente frágil. Veja uma resposta sobre Hyperic Sigar abaixo.
Michael Klishin 27/02
Aquele pid é bom para escrever em um arquivo de bloqueio como stackoverflow.com/a/9020391/1422630
Aquarius Power
111

Você poderia usar o JNA . Infelizmente, ainda não existe uma API JNA comum para obter o ID do processo atual, mas cada plataforma é bastante simples:

janelas

Certifique-se de ter jna-platform.jar:

int pid = Kernel32.INSTANCE.GetCurrentProcessId();

Unix

Declarar:

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

Então:

int pid = CLibrary.INSTANCE.getpid();

Java 9

No Java 9, a nova API do processo pode ser usada para obter o ID do processo atual. Primeiro, você pega um identificador para o processo atual e, em seguida, consulta o PID:

long pid = ProcessHandle.current().pid();
Luke Quinane
fonte
4
+1 Mas receio que, em um ambiente com restrição de segurança, ele não funcione (tomcat, WebLogic etc.).
ATorras
o getpid() solução também funciona para o OS X, com uma chamada JNA para a biblioteca "System".
Daniel Widdis
No entanto, eu recebo error: cannot find symbolpara jdk9
skytree
56

Aqui está um método backdoor que pode não funcionar com todas as VMs, mas deve funcionar no Linux e no Windows ( exemplo original aqui ):

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
fonte
2
muito bom, apenas verifiquei que ele funciona tanto no JRockit quanto nas JVMs do Hotspot.
Drupad Panchal
11
Solução agradável. Vou assumir que há um bom motivo para esse método (e outros da classe) não serem públicos e facilmente acessíveis, e estou curioso para saber o que é.
fragorl
2
A equipe Oracle Java anunciou que pretende ocultar todos os pacotes não-java (por exemplo, sun. *). Acredito que começando com o Java 10 (programado para 2018 - talvez Java 9). Se você possui uma implementação semelhante à acima, convém descobrir uma alternativa para que seu código não seja quebrado.
hfontanez
Não funciona para mim como o sol. * Os pacotes provavelmente estão removidos ou inacessíveis (o VMManagement não pode ser resolvido para um tipo).
Mike Stoddart
Devido à maneira como a reflexão funciona, o acesso ao sun.management. * Não é necessário. Basta executar o getDeclaredMethodno objeto retornado por jvm.get(runtime).
Andrew T Finnell
36

Tente Sigar . APIs muito extensas. Licença 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
fonte
6
Você teria muito mais votos positivos se explicasse como usar o SIGAR para isso #
Brad Mace
11
Link quebrado. Você dá um exemplo de como usar isso, mas existe alguma dependência para isso?
Jules
github.com/hyperic/sigar parece ser um local utilizável; isso também mostra que é um código nativo com ligações Java, o que pode torná-lo menos uma solução para muitos contextos.
Zastai 16/10/1918
26

O método a seguir tenta extrair o 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;
}

Basta ligar getProcessId("<PID>"), por exemplo.

Martin
fonte
6
O VisualVM usa código semelhante para obter PID próprio, verifique com.sun.tools.visualvm.application.jvm.Jvm # ApplicationSupport # createCurrentApplication (). Eles são os especialistas, portanto parece uma solução confiável e multiplataforma.
Espinosa
O @Espinosa VisualVM suporta apenas a execução em uma JVM OpenJDK / Oracle / GraalVM (a partir de 2020). Isso significa que eles podem fazer suposições de acordo. Se você fizer o mesmo, você se tornará dependente do fornecedor e seu código poderá quebrar se estiver executando, por exemplo, J9.
Thorbjørn Ravn Andersen
24

Para JVM mais antiga, no 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
fonte
52
Se você vai fazer isso, basta fazer int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());. Por que as girações extras?
precisa
2
Para os curiosos, o truque no comentário de @David realmente funciona. Alguém pode dizer o porquê? Algum tipo de mágica no getCanonicalFile()método que converte "self" no PID?
GreenGiant 2/12/12
7
O getCanonicalFile () resolve o link simbólico / proc / self / -> / proc / 1234, tente ls -al / proc / self #
Michael Michael
2
@DavidCitron +1! você está certo, eu não pensei em getCanonicalFile :-)
ggrandes
18

Você pode conferir o meu projeto: JavaSysMon no GitHub. Ele fornece a identificação do processo e várias outras coisas (uso da CPU, uso da memória) entre plataformas (atualmente Windows, Mac OSX, Linux e Solaris)

Jez Humble
fonte
É possível obter o PID de um processo iniciado pelo Java? Ou iniciar um processo "do seu jeito" para corrigir esse problema?
Panayotis
17

Desde o Java 9, existe um método Process.getPid () que retorna o ID nativo de um processo:

public abstract class Process {

    ...

    public long getPid();
}

Para obter o ID do processo Java atual, pode-se usar a ProcessHandleinterface:

System.out.println(ProcessHandle.current().pid());
ZhekaKozlov
fonte
2
Você pode explicar como obter uma instância de processo para o processo atual em execução no Java 9?
Augusto
Ótima resposta para o uso do Java 9 em 2016! Você pode adicionar um link para os javadocs? graças
crazyGuy
8

Em Scala:

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

Isso deve fornecer uma solução alternativa nos sistemas Unix até que o Java 9 seja lançado. (Eu sei, a pergunta era sobre Java, mas como não há uma pergunta equivalente para o Scala, eu gostaria de deixar isso para os usuários do Scala que possam encontrar a mesma pergunta.)

Florin T.
fonte
7

Para completar, existe um wrapper no Spring Boot para o

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

solução. Se um número inteiro for necessário, isso pode ser resumido na linha única:

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

Se alguém já usa a inicialização Spring, ele pode usar org.springframework.boot.ApplicationPid

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

O método toString () imprime o pid ou '???'.

Advertências sobre o ManagementFactory já são discutidas em outras respostas.

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

O mais recente que descobri é que existe uma propriedade do sistema chamada sun.java.launcher.pidque está disponível pelo menos no linux. Meu plano é usar isso e, se não for encontrado, use o JMX bean.

Jared
fonte
No meu caso com o Ubuntu 16.04 e Java 8, ele retorna nulo
david.perez
4

Depende de onde você está procurando as informações.

Se você estiver procurando as informações do console, poderá usar o comando jps. O comando fornece uma saída semelhante ao comando ps do Unix e vem com o JDK, pois acredito que o 1.5

Se você está procurando pelo processo, o RuntimeMXBean (como dito por Wouter Coekaerts) é provavelmente a sua melhor escolha. A saída de getName () no Windows usando o Sun JDK 1.6 u7 está no formato [PROCESS_ID] @ [MACHINE_NAME]. No entanto, você pode tentar executar jps e analisar o resultado disso:

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

Se executado sem opções, a saída deve ser a identificação do processo seguida pelo nome.

Ryan P
fonte
11
A ferramenta JPS usa a biblioteca jvmstat, parte do tools.jar. Veja meu exemplo ou consulte o código-fonte JPS: grepcode.com/file_/repository.grepcode.com/java/root/jdk/… . Não há necessidade de chamar o JPS como processo externo, use a biblioteca jvmstat diretamente.
Espinosa
4

Esse é o código que JConsole usa e potencialmente o jps e o VisualVM. Ele utiliza classes do sun.jvmstat.monitor.*pacote, 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;
    }
}

Existem poucas capturas:

  • o tool.jar é uma biblioteca distribuída com o Oracle JDK, mas não o JRE!
  • Você não pode obter tool.jar do repositório Maven; configurá-lo com o Maven é um pouco complicado
  • o tool.jar provavelmente contém código dependente de plataforma (nativo?) Por isso não é facilmente distribuível
  • Ele é executado sob a premissa de que todos os aplicativos JVM em execução (locais) são "monitoráveis". Parece que no Java 6 todos os aplicativos geralmente são (a menos que você configure ativamente o oposto)
  • Provavelmente funciona apenas para Java 6+
  • O Eclipse não publica a classe principal, portanto você não obterá facilmente o Eclipse PID Bug no MonitoredVmUtil?

ATUALIZAÇÃO: Acabei de verificar se o JPS usa dessa maneira, ou seja, a biblioteca Jvmstat (parte do tool.jar). Portanto, não há necessidade de chamar o JPS como processo externo, chame a biblioteca Jvmstat diretamente, como mostra meu exemplo. Você também pode obter uma lista de todas as JVMs em execução no host local dessa maneira. Veja o código fonte JPS :

Espinosa
fonte
se mainClass.getName () não funcionar, tente usar mainClass.getCanonicalName ();
Narendra Parmar
3

Estou adicionando isso a outras soluções.

com Java 10, para obter process id

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

Você pode tentar getpid()no JNR-Posix .

Ele possui um wrapper do Windows POSIX que chama getpid () da libc.

Kervin
fonte
1

Eu sei que esse é um encadeamento antigo, mas eu queria chamar a atenção de que a API para obter o PID (assim como outras manipulações do processo Java no tempo de execução) está sendo adicionada à classe Process no JDK 9: http: // openjdk. java.net/jeps/102

Brandon Heck
fonte
9
Uau, isso foi rápido. Levou apenas sete anos! D
Dmitry Shechtman
1

Encontrei uma solução que pode ser um pouco complicada e não tentei em outro sistema operacional que não o Windows 10, mas acho que vale a pena notar.

Se você estiver trabalhando com J2V8 e nodejs, poderá executar uma função javascript simples retornando o pid do processo java.

Aqui está um exemplo:

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

Aqui está a minha solução:

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
fonte
Este não verifica se o PID está em uso, mas se pid é igual pid processo atual
c0der
Qual é a solução correta para atualizar meu código?
PartialData
-6

Isso é o que eu usei quando tinha requisitos semelhantes. Isso determina o PID do processo Java corretamente. Deixe seu código java gerar um servidor em um número de porta predefinido e execute comandos do SO para descobrir o PID que está ouvindo na porta. Para Linux

netstat -tupln | grep portNumber
Subhash
fonte
3
Se você está chamando scripts shell, assim como você pode fazer algo mais simples como bash -c 'echo $PPID'ou o / proc respostas acima
Ricos