Как Java-программа может получить свой собственный идентификатор процесса?

341

Как я могу получить идентификатор моего процесса Java?

Я знаю, что есть несколько зависимых от платформы хаков, но я бы предпочел более общее решение.

Дмитрий Шехтман
источник
1
Ссылка на вопрос Linux: Как мне найти мой PID в Java или JRuby в Linux?
Ярекчек
4
Это должно быть исправлено в JDK9. openjdk.java.net/jeps/102
Андрей

Ответы:

322

Не существует независимого от платформы способа, который гарантированно работал бы во всех реализациях jvm. ManagementFactory.getRuntimeMXBean().getName()выглядит как лучшее (самое близкое) решение. Это коротко, и, вероятно, работает в каждой реализации в широком использовании.

В Linux + Windows он возвращает значение как 12345@hostname( 12345идентификатор процесса). Остерегайтесь, однако, в соответствии с документами , нет никаких гарантий относительно этого значения:

Возвращает имя, представляющее работающую виртуальную машину Java. Возвращенная строка имени может быть любой произвольной строкой, и реализация виртуальной машины Java может выбрать встраивание полезной информации для конкретной платформы в возвращенную строку имени. Каждая запущенная виртуальная машина может иметь свое имя.

В Java 9 может использоваться новый API процесса :

long pid = ProcessHandle.current().pid();
Wouter Coekaerts
источник
2
Это решение действительно хрупкое. Смотрите ответ о Hyperic Sigar ниже.
Михаил Клишин
этот pid хорош для записи в файл блокировки, например stackoverflow.com/a/9020391/1422630
Aquarius Power
111

Вы могли бы использовать JNA . К сожалению, пока нет общего JNA API для получения текущего идентификатора процесса, но каждая платформа довольно проста:

Windows

Убедитесь, что у вас есть jna-platform.jar:

int pid = Kernel32.INSTANCE.GetCurrentProcessId();

Юникс

Объявляет:

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

Затем:

int pid = CLibrary.INSTANCE.getpid();

Java 9

В Java 9 новый API процесса может использоваться для получения текущего идентификатора процесса. Сначала вы берете дескриптор текущего процесса, затем запрашиваете PID:

long pid = ProcessHandle.current().pid();
Люк Куинан
источник
4
+1 Но я боюсь, что в условиях безопасности это не должно работать (tomcat, WebLogic и т. Д.).
ATorras
getpid()Решение также работает на OS X, с помощью вызова ЮНА в библиотеку «System».
Даниэль Виддис
Тем не менее, я получаю error: cannot find symbolдля jdk9
Skytree
56

Вот метод backdoor, который может не работать со всеми виртуальными машинами, но должен работать как на Linux, так и на Windows ( оригинальный пример здесь ):

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
очень хорошо, только что проверил, что он работает как на JRockit, так и на JVM Hotspot.
Drupad Panchal
1
Хороший обходной путь. Я собираюсь предположить, что есть веская причина, почему этот метод (и другие в классе) не являются общедоступными и легкодоступными, и мне любопытно узнать, что это такое.
fragorl
2
Команда Oracle по Java объявила, что намерена скрыть все не-Java-пакеты (например, Sun. *). Я полагаю, начиная с Java 10 (запланировано на 2018 год - может быть, Java 9). Если у вас есть реализация, аналогичная приведенной выше, вы можете найти альтернативу, чтобы ваш код не сломался.
hfontanez
У меня не работает, так как пакеты sun. *, Вероятно, удалены или недоступны (VMManagement не может быть определен для типа).
Майк Стоддарт
Из-за того, как работает отражение, доступ к sun.management. * Не нужен. Просто выполните getDeclaredMethodобъект, возвращенный jvm.get(runtime).
Эндрю Т Финнелл
36

Попробуй Сигар . очень обширные API. Лицензия 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
У вас было бы намного больше голосов, если бы вы объяснили, как использовать SIGAR для этого
Брэд Мейс
1
Ссылка не работает. Вы привели пример того, как использовать это, но есть ли для него зависимость maven?
Жюль
github.com/hyperic/sigar - удобное для использования место; это также показывает, что это нативный код с привязками Java, что может сделать его менее подходящим для многих контекстов.
Застай
26

Следующий метод пытается извлечь PID из 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;
}

Просто позвоните getProcessId("<PID>"), например.

Мартин
источник
6
VisualVM использует подобный код для получения собственного PID, проверьте com.sun.tools.visualvm.application.jvm.Jvm # ApplicationSupport # createCurrentApplication (). Они являются экспертами, поэтому это выглядит как надежное, кроссплатформенное решение.
Эспиноза
@Espinosa VisualVM поддерживает работу только на JVM OpenJDK / Oracle / GraalVM (по состоянию на 2020 год). Это означает, что они могут делать предположения соответственно. Если вы сделаете то же самое, вы станете зависимым от поставщика, и ваш код может сломаться, например, при работе на J9.
Торбьерн Равн Андерсен
24

Для старых JVM, в 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
Если вы собираетесь это сделать, тогда просто делайте int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());. Почему лишние вращения?
Дэвид Цитрон
2
Любопытно, что трюк в комментарии @David действительно работает. Может кто-нибудь сказать почему? Какая-то магия в getCanonicalFile()методе, который преобразует «я» в PID?
GreenGiant,
7
getCanonicalFile () разрешает символическую ссылку / proc / self / -> / proc / 1234, попробуйте ls -al / proc / self
Майкл,
2
@DavidCitron +1! Вы правы, я не думаю, что в getCanonicalFile :-)
ggrandes
18

Вы можете проверить мой проект: JavaSysMon на GitHub. Он предоставляет идентификатор процесса и кучу других вещей (использование процессора, использование памяти) кроссплатформенных (в настоящее время Windows, Mac OSX, Linux и Solaris)

Джез Хамбл
источник
Можно ли получить PID процесса, запущенного Java? Или начать процесс "по-своему", чтобы исправить эту проблему?
Панайотис
17

Начиная с Java 9 существует метод Process.getPid (), который возвращает собственный идентификатор процесса:

public abstract class Process {

    ...

    public long getPid();
}

Чтобы получить идентификатор текущего процесса Java, можно использовать ProcessHandleинтерфейс:

System.out.println(ProcessHandle.current().pid());
ZhekaKozlov
источник
2
Можете ли вы объяснить, как получить экземпляр Process для текущего запущенного процесса в Java 9?
Августо
Отличный ответ за использование Java 9 в 2016 году! Можете ли вы добавить ссылку на Javadocs? спасибо
crazyGuy
8

В Скала:

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

Это должно дать вам обходной путь в системах Unix до выпуска Java 9. (Я знаю, что вопрос был о Java, но поскольку у Scala нет аналогичного вопроса, я хотел оставить это для пользователей Scala, которые могут наткнуться на тот же вопрос.)

Флорин Т.
источник
7

Для полноты в Spring Boot есть обертка для

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

решение. Если требуется целое число, то оно может быть суммировано до однострочного:

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

Если кто-то уже использует загрузку Spring, он / она может использовать org.springframework.boot.ApplicationPid

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

Метод toString () печатает pid или «???».

Предостережения, использующие ManagementFactory, уже обсуждались в других ответах.

l00tr
источник
7
java.lang.management.ManagementFactory.getRuntimeMXBean().getName().split("@")[0]
Markus
источник
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
источник
5

Последнее я обнаружил, что есть свойство системы называется sun.java.launcher.pid, которая доступна по крайней мере , на Linux. Мой план состоит в том, чтобы использовать это, и если не найдено, чтобы использовать JMX bean.

Джаред
источник
В моем случае с Ubuntu 16.04 и Java 8 он возвращает null
david.perez
4

Это зависит от того, откуда вы ищете информацию.

Если вы ищете информацию из консоли, вы можете использовать команду jps. Команда выводит данные, аналогичные команде Unix ps, и поставляется с JDK, так как я считаю, что 1.5

Если вы смотрите на процесс, RuntimeMXBean (как сказал Wouter Coekaerts), вероятно, ваш лучший выбор. Вывод функции getName () в Windows с использованием Sun JDK 1.6 u7 имеет вид [PROCESS_ID] @ [MACHINE_NAME]. Однако вы можете попытаться выполнить jps и проанализировать результат из этого:

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

Если запустить без параметров, вывод должен быть идентификатор процесса, а затем имя.

Райан П
источник
1
Инструмент JPS использует библиотеку jvmstat, часть tools.jar. Посмотрите мой пример или посмотрите исходный код JPS: grepcode.com/file_/repository.grepcode.com/java/root/jdk/… . Нет необходимости вызывать JPS как внешний процесс, используйте библиотеку jvmstat напрямую.
Эспиноза
4

Это код JConsole, и, возможно, JPS и VisualVM использует. Он использует классы из sun.jvmstat.monitor.*пакета, из 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;
    }
}

Есть несколько уловов:

  • Это tool.jarбиблиотека, распространяемая вместе с Oracle JDK, но не JRE!
  • Вы не можете получить tool.jarот репозитория Maven; настроить его с Maven немного сложнее
  • tool.jar, Вероятно , содержит зависимый (родной?) Код платформы , так что не легко распространяемый
  • Он работает при условии, что все (локальные) запущенные приложения JVM являются «контролируемыми». Похоже, что из Java 6 все приложения, как правило, (если вы не настроили противоположное)
  • Вероятно, это работает только для Java 6+
  • Eclipse не публикует основной класс, поэтому вы не получите Eclipse PID легко Ошибка в MonitoredVmUtil?

ОБНОВЛЕНИЕ: я только что дважды проверил, что JPS использует этот способ, то есть библиотека Jvmstat (часть tool.jar). Так что нет необходимости вызывать JPS как внешний процесс, напрямую вызывать библиотеку Jvmstat, как показывает мой пример. Вы также можете получить список всех JVM, запущенных на localhost. Смотрите исходный код JPS :

Эспиноса
источник
если mainClass.getName () не работает, попробуйте использовать mainClass.getCanonicalName ();
Нарендра Пармар
3

Я добавляю это к другим решениям.

с Java 10, чтобы получить process id

final RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
final long pid = runtime.getPid();
out.println("Process ID is '" + pid);
mrsrinivas
источник
2

Основываясь на ответе Эшвина Джаяпракаша (+1) о лицензированном Apache 2.0 SIGAR, я использую его для получения только PID текущего процесса:

import org.hyperic.sigar.Sigar;

Sigar sigar = new Sigar();
long pid = sigar.getPid();
sigar.close();

Хотя он работает не на всех платформах, он работает на Linux, Windows, OS X и различных платформах Unix, как указано здесь .

tomsv
источник
1

Вы можете попробовать getpid()в JNR-Posix .

Он имеет оболочку Windows POSIX, которая вызывает getpid () из libc.

Kervin
источник
1

Я знаю, что это старый поток, но я хотел сказать, что API для получения PID (а также другие манипуляции с процессом Java во время выполнения) добавляется в класс Process в JDK 9: http: // openjdk. java.net/jeps/102

Брэндон Хек
источник
9
Вау, это было быстро Это заняло всего около семи лет! Dmitry
Дмитрий Шехтман
1

Я нашел решение, которое может показаться немного странным, и я не пробовал его на других ОС, кроме Windows 10, но, думаю, стоит обратить на это внимание.

Если вы обнаружите, что работаете с J2V8 и nodejs, вы можете запустить простую функцию javascript, возвращающую вам pid процесса java.

Вот пример:

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
источник
-1

Вот мое решение:

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
источник
Это не проверяет, используется ли pid, но если pid равен текущему pid процесса
c0der
Какое правильное решение, чтобы я мог обновить свой код?
PartialData
-6

Это то, что я использовал, когда у меня было подобное требование. Это правильно определяет PID процесса Java. Позвольте вашему Java-коду порождать сервер по заранее определенному номеру порта, а затем выполнять команды ОС, чтобы выяснить PID, прослушивающий порт. Для Linux

netstat -tupln | grep portNumber
Subhash
источник
3
Если вы вызываете сценарии оболочки, вы также можете сделать что-то более простое, например, bash -c 'echo $PPID'или ответы
Rich