Saturday, March 03, 2007

How a Java Application Can Discover its Process ID (PID)

Occasionally it is important for an application to know its PID, specially if this application cooperates with other non-java applications. Currently there is no direct support for retrieving an application's process id by using standard Java api (this might change in the future if RFEs like 4250622, 4244896 or 4890847 are resolved).

I found five ways how to get the PID from my Java code:
  1. Using the java management and monitoring API (java.lang.management):
    ManagementFactory.getRuntimeMXBean().getName();
    returns something like:
    28906@localhost
    where 28906 is the PID of JVM's process, which is in fact the PID of my app.

    This hack is JVM dependent and I tested it only with Sun's JVM.

    From Javadocs for getName() method of RuntimeMXBean:
    Returns the name representing the running Java virtual machine. The returned name string can be any arbitrary string and a Java virtual machine implementation can choose to embed platform-specific useful information in the returned name string. Each running virtual machine could have a different name.
    So even though this approach is the most comfortable one, your app can break if the implementation of this method changes.
  2. Using shell script in addition to Java properties Start your app with a shellscript like this:
    exec java -Dpid=$$ -jar /Applications/bsh-2.0b4.jar
    then in java code call:
    System.getProperty("pid");
  3. Using shell script's $! facility as described on this blog - this approach is fine if all you want is to create a pid file.
  4. Using Java Native Interface (JNI) - a very cumbersome and platform dependent solution.
  5. Using $PPID and Runtime.exec(String[]) method - described in detail in this post
    import java.io.IOException;
    
    public class Pid {
    public static void main(String[] args) throws IOException {
      byte[] bo = new byte[100];
      String[] cmd = {"bash", "-c", "echo $PPID"};
      Process p = Runtime.getRuntime().exec(cmd);
      p.getInputStream().read(bo);
      System.out.println(new String(bo));
    }
    }
    
It must be said that none of these approaches is perfect and each of them has some drawbacks.

I know that one of Sun's main priorities for Java is cross-platform compatibility, but I have to agree with the comments on the RFEs above which support the addition of a getPid() method to JDK. I especially like this one:
Come on Sun, this started 4.5 years ago. Give us the
PID. We need it. We want it. We demand it.
And another thing ... you will save a lot of developers a
lot of time currently spent searching through the
documentation trying to find a getPID method that isn't
there!
Posted by Rarb@GB on 05-MAR-2004

UPDATE (06-03-05): added 5th approach

32 comments:

kyug said...

Thanks, good overview! Two things:
1. I think it makes perfect sense, that it's not there, there are many OS that just simply doesn't have it, right! So the only place where sun can add it is sun.misc.* package.

2. Why do you think JNI is bad solution, it is actually the only most platform independent one. Well if you know how to write a platform independent code! And you don't need to do those obscure hacks with bash. Come on, don't be afraid of C :)

Igor Minar said...

re 1) most of the OSes out there have a concept of processes and each of them assigns some kind of ID for these processes. And to make it as independent as possible Java should deal with these IDs as with Strings.

re 2) I'm not afraid of C, it's just that once you start using JNI you have to compile your JNI/C code for each platform you are targeting, which complicates distribution and deployment of such an application

marius said...

Thanks for the overview.
But do you have any idea on how to get the PID of the process that is created by Runtime.exec(command) without relying on bash and command structure?
e.g. I need it to work on windows, windows don't seem to maintain the concept of parent-child, i.e. I could not find any function in Windows API on how to get parent PID or PIDs of children.
So even with JNI hacks, the only option seems to identify processes by executable name, but then there might be several such processes... :(

Anonymous said...

Thanks very much for the overview! I have tried the "echo $PPID" method using the following program (slightly adapted from your example - I packaged it as a jar file wit the ptest class as main class):

package pidtest;

import java.io.IOException;

public class ptest
{
public static void main (String [] args) throws IOException
{
String [] cmd =
{
"bash", "-c", "echo $PPID"
};
int i = 0;
byte [] bo = null;

for (i = 1; i <= 10; i++)
{
bo = new byte [100];
Process p = Runtime.getRuntime ().exec (cmd);
p.getInputStream ().read (bo);
System.out.println (new String (bo));
}
}
}


This is the output I got:

peter@peter:~/workspace/pidtest$ java -jar ./pidtest.jar
5539
5541
5543
5545
5547
5549
5551
5553
5555
5557

Now, in my understanding I should have got values that are all the same, as I ran one java process (one process which called bash -c "echo $PPID" repeatedly. Since all calls to bash were issued from the same process I would have expected the same value for all calls.

Is my understanding flawed in some way?

Peter

please note:

Sorry about the badly formatted code - I have tried the pre HTML tag, but it didn't work.

I used the 'anonymous' identity to make it a bit more difficult for spammer's robots - I receive a lot of spam per day. My mail address is:

peter {d0t} hoppe {a_t} gmail {d0t} com

Igor Minar said...

Hi Peter,

the results you are getting are interesting.

Why I run your program I get:
$ java ptest
23568
23568
23568
23568
23568
23568
23568
23568
23568
23568

This is on MacOSX 10.4.10 Intel, java:
$ java -version
java version "1.5.0_07"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_07-154)
Java HotSpot(TM) Client VM (build 1.5.0_07-87, mixed mode, sharing)


To me it sounds like either your OS, shell or java doesn't do something right.


PS: email addresses should never be displayed on my blog, so you don't need to worry about that.

cheers,
Igor

Anonymous said...

Thanks, Igor!

It's perfectly possible that something isn't right in my environment. I did the same task in a shell script and got the same PID when calling echo $PPID several times. So I guess it's something with my Java VM. I am running:

peter@peter:~$ java -version
java version "1.5.0_06"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05)
Java HotSpot(TM) Client VM (build 1.5.0_06-b05, mixed mode, sharing)
peter@peter:~$

My OS is:

peter@peter:~$ cat /etc/issue
Debian GNU/Linux 3.1 \n \l

and I think it's a 2.4 kernel. I suspect it's something with the way Java works in this particular environment (spawning a new thread per call to bash? I think I heard something along the lines that Java threads have got their own PID).

Thanks very much for your answer - and also for the comprehensive information!

Peter

John said...

on linux there is /proc/ (procfs)

So you can use
int pid = Integer.parseInt( ( new File("/proc/self")).getCanonicalFile().getName() );

Anonymous said...

Dear John,

very many thanks for that solution! It works excellently! I hope is that this method is portable across Linux distributions and here to stay for the future. Forgive my ignorance on this...

I have tried the method with a test program using the proposed code and got the following output:

18810
18810
18810
18810
18810
18810
18810
18810
18810
18810

/*******************************************************************************
* tpidtest.java
* -------------------
* initial author : Peter Hoppe
* copyright : (C) 2007
******************************************************************************/

/*******************************************************************************
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 675 Mass
* Ave, Cambridge, MA 02139, USA.
*
******************************************************************************/


package org.rww.mmailstarter.test;

import java.io.File;
import java.io.IOException;

/**
* Prints (to stdout) the PID of the java process that runs this program.
*
* The PID discovery method is specific to the Linux Operating system and
* may not work on other operating systems.
*/
public class TPidTest
{
public static void main (String [] args)
{
int i = 0;
int pid = 0;

try
{
for (i = 0; i < 10; i++)
{
pid = Integer.parseInt( ( new File("/proc/self")).getCanonicalFile().getName() );
System.out.println (pid);
}
}
catch (NumberFormatException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}

Anonymous said...

Reflection will also work. On UNIX the subclass of Process has a private field with the PID that the default JVM security settings allow you to get via reflection:

static int getPID(Process process)
throws IllegalAccessException, IllegalArgumentException,
NoSuchFieldException, SecurityException
{
Field field = process.getClass().getDeclaredField("pid");
field.setAccessible(true);
return field.getInt(process);
}

Anonymous said...

Excellent man. The reflection solution works pretty good and thanks a lot.

Anonymous said...

I found that the method retrieving name from runtime mbean works fine under Linux and Mac OS X, for Windows I have not an test enviroment.
Great solution, Thanks a lot.

Jan

Charles Roth said...

Igor -- thanks for starting this.

I wanted to find out not only the process id of the entire process, but also the thread id (aka "lightweight process id") of new threads.

I found a kluge that works, most of the time, under linux -- described in detail at http://thedance.net/~roth/TECHBLOG/threadtracker.html

Basically it involves reading /proc/self/task multiple times to look for new threads as they get created. This is linux-only, but could possibly be adapted to other *nixes.

Cheers!

Igor Minar said...

Hi Charles,

your solution is not pretty, but hey in this case it's better than nothing ;-)

/i

Charles Roth said...

Hi, Igor -- you have a beautiful talent for understatement! ("not pretty", indeed!)

Is there something like /proc/self for Solaris?

Roger Pack said...

worked perfectly on linux x86:
12766@hostname

Al said...

Excellent overview. I think the Java management API is the best approach, assuming your JVM is 1.5 or higher. I've used it on Windows XP (Sun JVM 1.5) and AIX 5.2 (IBM JVM 1.5).

Anonymous said...

Thanx dude... really helped me out!!

rgomes1997 said...

The program above prints different values for a PID because... well... they are really different process IDs!

peter@peter:~/workspace/pidtest$ java -jar ./pidtest.jar
5539
5541
5543
5545
5547
5549
5551
5553
5555
5557

On Unix systems, every time an image is loaded into memory, a new process is created.

When you run ...

Process p = Runtime.getRuntime ().exec (cmd);

... several times, you are creating several processes and you should expect different PIDs. This is perfectly normal.

Richard Gomes
http://www.jquantlib.org/index.php/User:RichardGomes
twitter: frgomes

JQuantlib is a Library for Quantitative Finance written in Java.
http://www.jquantlib.org/
twitter: jquantlib

Anonymous said...

If the JVM starts a new bash process, wouldn't the $PPID returned be the pid of the bash process, rather than the pid of the JVM?

Igor Minar said...

Keep in mind that $PPID returns the pid of the parent process. So it won't return the process of the newly spawned process, but rather the pid of its parent, which is the JVM process.

Byron Nevins said...

One more method -- exec jps (available in all JDKs) and find the pid by the classname.

What nonsense this is!!

As to the argument of there maybe being an OS that does not use pid -- no problem. Return -1 in that case! Why should we use the lowest common denominator?

Igor Minar said...

jps is the best way to find a pid for an external jvm process. The methods I captured in my blog post deal with discovering the process pid from within the java application.

vishnu said...

Hi all,

I tried the reflection technique , it works only on linux.

We get 'java.lang.NoSuchFieldException: pid'
on windows.

with regards,
ch vishnu213

Jez Humble said...

Hey Igor

You can also check out my project for monitoring performance in Java. I just added a call for pid: JavaSysMon at GitHub

Brett said...

nice summary Igor.

I'm using glassfish v3 and really wanted the java process to write its pid.

so I wired up a an osgi Activator to write pidfile on start.

Igor Minar said...

Cool Brett, I'm glad that it was useful. Too bad that even after 3 years, there is no hint from Sun/Oracle that we'll ever get a proper api to do this.

Laurent Caillette said...

Hi Igor,

I think I've found a 6^th way. It's based on the Attach API that didn't exist at the time you posted this entry. "Attach API"
http://blogs.sun.com/CoreJavaTechTips/entry/the_attach_api
is like a Java API to list Java processes like JPS does. In addition, one may attach to JVMs, query for system properties and load Java agents. The ``VirtualMachineDescriptor`` really looks like a String-based PID. By now it's a Sun (mean: Oracle) specific API. This doesn't mean other JVM don't support it.

That's extremely cool stuff when it comes to kill JVMs spawned with ``ProcessBuilder``. After tagging them with a special system property, you can shut them down gently by loading an agent that calls ``System.exit``. At least it's supposed to be cross-platform (disclaimer: I didn't try it for now).

Cheers,

Laurent Caillette

Mail written in Novelang

Igor Minar said...

Excellent find Laurent. Thanks for sharing.

Pawel said...

Hi, another useful approach may be by using JNA(Java Native Access) library (https://jna.dev.java.net/)

It is, however platform dependent. If you work under M$ all code is:

Kernel32.INSTANCE.GetCurrentProcessId();

If you work under UNIX, the code will be different and not as simple, but JNA allows you to use native libraries quite easily under most OS.

Anonymous said...

The 6th way is useless unless you know the pid before you run it which brings us full circle. jvisualvm uses the RuntimeMXBean.getName() method to find a list of vms to attach to and it must parse the pid out of those names. This seems to be the defacto standard.
By the way, All machines have a pid for each process. Otherwise they couldn't track processes at all. The difference is whether they expose them.

Anonymous said...

The latest I discovered is the system property sun.java.launcher.pid which is set on at least linux platforms JDKs. I haven't tried other Unix. It is not set on Windows. I was going to query that system property and if it isn't set go to the defacto of looking at the JMX Bean.

Julien Viet said...

I found a (hackish and fun) way using the JVM attach API, you can read more about it there : https://github.com/vietj/PID