Java标准库里常见的公有API确实是没有获取当前进程的ID的方法,有时候挺郁闷的,就是需要自己的PID。
于是有各种workaround,其中有很靠谱的通过JNI调用外部的C/C++扩展,然后调用操作系统提供的相应API去获取PID;也有些不怎么靠谱的hack。这里要介绍的就是后者之一,只在Sun JDK或兼容的JDK上有效的方法。
代码例子如下:
使用Sun JDK里RuntimeMXBean.getName()方法的实现,可以很轻松的拿到自身的PID。
运行这个程序可以看到类似以下的输出:
这个时候再跑一个jps来看看当前都有哪些Java进程的话,可以看到:
嗯……这PID没错。
这PID是哪儿来的呢?先看RuntimeMXBean实例的来源,java.lang.management.ManagementFactory:
可以看到它依赖了sun.management.ManagementFactory,于是看看对应的实现:
这里可以发现实现RuntimeMXBean接口的是sun.management.RuntimeImpl。它的实现是:
OK,看到getName()返回的是VMManagement.getVmId()的返回值,再跟过去看:
OK,这里可以看到getVmId()返回过来的字符串确实是"pid@hostname"形式的。再追下去,看看native一侧是如何获取PID的话:
j2se/src/share/native/sun/management/VMManagementImpl.c
于是继续跟,
hotspot/src/share/vm/services/management.cpp
接下来os::current_process_id()的实现就是每个操作系统不同的了。
在Linux上是:
hotspot/src/os/linux/vm/os_linux.cpp
在Windows上是:
=================================================================
好吧其实我是在HotSpot的源码里搜pid然后慢慢找出JMM代码里有调用过os::current_process_id(),然后才一步步向上找到对应的Java API。刚才问毕玄老大有没有见过在Java代码里获取PID的办法,才得知原来 以前有人总结过几种办法,其中第一个就是本文提到的这个。嘛,需求是一直有的,这种功能自然是早该有人捣腾过了。
于是有各种workaround,其中有很靠谱的通过JNI调用外部的C/C++扩展,然后调用操作系统提供的相应API去获取PID;也有些不怎么靠谱的hack。这里要介绍的就是后者之一,只在Sun JDK或兼容的JDK上有效的方法。
代码例子如下:
- import java.lang.management.ManagementFactory;
- import java.lang.management.RuntimeMXBean;
- public class ShowOwnPID {
- public static void main(String[] args) throws Exception {
- int pid = getPid();
- System.out.println("pid: " + pid);
- System.in.read(); // block the program so that we can do some probing on it
- }
- private static int getPid() {
- RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
- String name = runtime.getName(); // format: "pid@hostname"
- try {
- return Integer.parseInt(name.substring(0, name.indexOf('@')));
- } catch (Exception e) {
- return -1;
- }
- }
- }
使用Sun JDK里RuntimeMXBean.getName()方法的实现,可以很轻松的拿到自身的PID。
运行这个程序可以看到类似以下的输出:
- D:\experiment>java ShowOwnPID
- pid: 11704
这个时候再跑一个jps来看看当前都有哪些Java进程的话,可以看到:
- D:\experiment>jps
- 7888 Jps
- 11704 ShowOwnPID
嗯……这PID没错。
这PID是哪儿来的呢?先看RuntimeMXBean实例的来源,java.lang.management.ManagementFactory:
- package java.lang.management;
- // ...
- public class ManagementFactory {
- // ...
- /**
- * Returns the managed bean for the runtime system of
- * the Java virtual machine.
- *
- * @return a {@link RuntimeMXBean} object for the Java virtual machine.
- */
- public static RuntimeMXBean getRuntimeMXBean() {
- return sun.management.ManagementFactory.getRuntimeMXBean();
- }
- // ...
- }
可以看到它依赖了sun.management.ManagementFactory,于是看看对应的实现:
- package sun.management;
- import java.lang.management.*;
- // ...
- import static java.lang.management.ManagementFactory.*;
- public class ManagementFactory {
- private ManagementFactory() {};
- private static VMManagement jvm;
- private static RuntimeImpl runtimeMBean = null;
- public static synchronized RuntimeMXBean getRuntimeMXBean() {
- if (runtimeMBean == null) {
- runtimeMBean = new RuntimeImpl(jvm);
- }
- return runtimeMBean;
- }
- static {
- AccessController.doPrivileged(new LoadLibraryAction("management"));
- jvm = new VMManagementImpl();
- }
- // ...
- }
这里可以发现实现RuntimeMXBean接口的是sun.management.RuntimeImpl。它的实现是:
- package sun.management;
- import java.lang.management.RuntimeMXBean;
- // ...
- /**
- * Implementation class for the runtime subsystem.
- * Standard and committed hotspot-specific metrics if any.
- *
- * ManagementFactory.getRuntimeMXBean() returns an instance
- * of this class.
- */
- class RuntimeImpl implements RuntimeMXBean {
- private final VMManagement jvm;
- private final long vmStartupTime;
- /**
- * Constructor of RuntimeImpl class.
- */
- RuntimeImpl(VMManagement vm) {
- this.jvm = vm;
- this.vmStartupTime = jvm.getStartupTime();
- }
- public String getName() {
- return jvm.getVmId();
- }
- // ...
- }
OK,看到getName()返回的是VMManagement.getVmId()的返回值,再跟过去看:
- package sun.management;
- import java.net.InetAddress;
- import java.net.UnknownHostException;
- // ...
- class VMManagementImpl implements VMManagement {
- // ...
- public String getVmId() {
- int pid = getProcessId();
- String hostname = "localhost";
- try {
- hostname = InetAddress.getLocalHost().getHostName();
- } catch (UnknownHostException e) {
- // ignore
- }
- return pid + "@" + hostname;
- }
- private native int getProcessId();
- // ...
- }
OK,这里可以看到getVmId()返回过来的字符串确实是"pid@hostname"形式的。再追下去,看看native一侧是如何获取PID的话:
j2se/src/share/native/sun/management/VMManagementImpl.c
- JNIEXPORT jint JNICALL
- Java_sun_management_VMManagementImpl_getProcessId
- (JNIEnv *env, jobject dummy)
- {
- jlong pid = jmm_interface->GetLongAttribute(env, NULL,
- JMM_OS_PROCESS_ID);
- return (jint) pid;
- }
于是继续跟,
hotspot/src/share/vm/services/management.cpp
- static jlong get_long_attribute(jmmLongAttribute att) {
- switch (att) {
- // ...
- case JMM_OS_PROCESS_ID:
- return os::current_process_id();
- // ...
- default:
- return -1;
- }
- }
- JVM_ENTRY(jlong, jmm_GetLongAttribute(JNIEnv *env, jobject obj, jmmLongAttribute att))
- if (obj == NULL) {
- return get_long_attribute(att);
- } else {
- // ...
- }
- return -1;
- JVM_END
接下来os::current_process_id()的实现就是每个操作系统不同的了。
在Linux上是:
hotspot/src/os/linux/vm/os_linux.cpp
- static pid_t _initial_pid = 0;
- int os::current_process_id() {
- // Under the old linux thread library, linux gives each thread
- // its own process id. Because of this each thread will return
- // a different pid if this method were to return the result
- // of getpid(2). Linux provides no api that returns the pid
- // of the launcher thread for the vm. This implementation
- // returns a unique pid, the pid of the launcher thread
- // that starts the vm 'process'.
- // Under the NPTL, getpid() returns the same pid as the
- // launcher thread rather than a unique pid per thread.
- // Use gettid() if you want the old pre NPTL behaviour.
- // if you are looking for the result of a call to getpid() that
- // returns a unique pid for the calling thread, then look at the
- // OSThread::thread_id() method in osThread_linux.hpp file
- return (int)(_initial_pid ? _initial_pid : getpid());
- }
- // this is called _before_ the most of global arguments have been parsed
- void os::init(void) {
- // With LinuxThreads the JavaMain thread pid (primordial thread)
- // is different than the pid of the java launcher thread.
- // So, on Linux, the launcher thread pid is passed to the VM
- // via the sun.java.launcher.pid property.
- // Use this property instead of getpid() if it was correctly passed.
- // See bug 6351349.
- pid_t java_launcher_pid = (pid_t) Arguments::sun_java_launcher_pid();
- _initial_pid = (java_launcher_pid > 0) ? java_launcher_pid : getpid();
- // ...
- }
在Windows上是:
- static int _initial_pid = 0;
- int os::current_process_id()
- {
- return (_initial_pid ? _initial_pid : _getpid());
- }
- // this is called _before_ the global arguments have been parsed
- void os::init(void) {
- _initial_pid = _getpid();
- // ...
- }
=================================================================
好吧其实我是在HotSpot的源码里搜pid然后慢慢找出JMM代码里有调用过os::current_process_id(),然后才一步步向上找到对应的Java API。刚才问毕玄老大有没有见过在Java代码里获取PID的办法,才得知原来 以前有人总结过几种办法,其中第一个就是本文提到的这个。嘛,需求是一直有的,这种功能自然是早该有人捣腾过了。