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

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

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

22 ответа

Решение

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

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

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

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

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

Вы могли бы использовать 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();

Вот метод 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);

Попробуйте Сигар. очень обширные 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();
    }

Следующий метод пытается извлечь 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>"), например.

Для старых 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";
}

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

public abstract class Process {

    ...

    public long getPid();
}

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

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

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

java.lang.management.ManagementFactory.getRuntimeMXBean().getName().split("@")[0]

В Скала:

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

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

Для полноты в 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, уже обсуждались в других ответах.

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

Добавление к другим решениям.

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

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

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

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

Если вы ищете информацию из консоли, вы можете использовать команду 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);

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

Это код 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:

Основано на ответе Эшвина Джаяпракаша (+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, как указано здесь.

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

Я нашел решение, которое может показаться немного странным, и я не пробовал его на других ОС, кроме 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();
}

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

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

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

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

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

netstat -tupln | grep portNumber
Другие вопросы по тегам