Bagaimana program Java mendapatkan ID prosesnya sendiri?

Jawaban:

322

Tidak ada cara platform-independen yang dapat dijamin untuk bekerja di semua implementasi jvm. ManagementFactory.getRuntimeMXBean().getName()sepertinya solusi terbaik (terdekat). Ini pendek, dan mungkin bekerja di setiap implementasi yang digunakan secara luas.

Di linux + windows, ia mengembalikan nilai seperti 12345@hostname( 12345menjadi id proses). Namun berhati-hatilah bahwa menurut dokumen , tidak ada jaminan tentang nilai ini:

Mengembalikan nama yang mewakili mesin virtual Java yang sedang berjalan. String nama yang dikembalikan dapat berupa sembarang string dan implementasi mesin virtual Java dapat memilih untuk menanamkan informasi berguna platform-spesifik dalam string nama yang dikembalikan. Setiap mesin virtual yang berjalan dapat memiliki nama yang berbeda.

Di Java 9 , API proses baru dapat digunakan:

long pid = ProcessHandle.current().pid();
Wouter Coekaerts
sumber
2
Solusi ini sangat rapuh. Lihat jawaban tentang Hyperic Sigar di bawah ini.
Michael Klishin
pid itu baik untuk ditulis pada file kunci sebagai stackoverflow.com/a/9020391/1422630
Aquarius Power
111

Anda bisa menggunakan JNA . Sayangnya tidak ada API JNA umum untuk mendapatkan ID proses saat ini, tetapi setiap platform cukup sederhana:

Windows

Pastikan Anda sudah jna-platform.jar:

int pid = Kernel32.INSTANCE.GetCurrentProcessId();

Unix

Menyatakan:

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

Kemudian:

int pid = CLibrary.INSTANCE.getpid();

Jawa 9

Di bawah Java 9 API proses baru dapat digunakan untuk mendapatkan ID proses saat ini. Pertama, Anda mengambil pegangan untuk proses saat ini, lalu meminta PID:

long pid = ProcessHandle.current().pid();
Luke Quinane
sumber
4
+1 Tetapi saya khawatir bahwa dalam lingkungan yang dibatasi keamanan tidak akan berfungsi (kucing jantan, WebLogic, dll.).
ATorras
The getpid()solusi juga bekerja untuk OS X, dengan panggilan JNA ke perpustakaan "Sistem".
Daniel Widdis
Namun, saya mendapatkan error: cannot find symboluntuk jdk9
skytree
56

Berikut adalah metode backdoor yang mungkin tidak berfungsi dengan semua VM tetapi harus bekerja pada linux dan windows ( contoh asli di sini ):

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
sumber
2
sangat bagus, baru diverifikasi bahwa ia berfungsi baik pada JRockit maupun JVM Hotspot.
Drupad Panchal
1
Penanganan yang bagus. Saya akan berasumsi ada alasan bagus mengapa metode ini (dan yang lainnya di kelas) tidak terbuka untuk umum dan mudah diakses, dan saya ingin tahu apa itu.
fragorl
2
Tim Oracle Java telah mengumumkan bahwa mereka bermaksud untuk menyembunyikan semua paket non-java (mis. Sun. *) Saya percaya memulai dengan Java 10 (dijadwalkan untuk 2018 - mungkin Java 9). Jika Anda memiliki implementasi yang serupa dengan yang di atas, Anda mungkin ingin mencari alternatif agar kode Anda tidak rusak.
hfontanez
Tidak berfungsi untuk saya baik karena matahari. * Paket mungkin dihapus atau tidak dapat diakses (VMManagement tidak dapat diselesaikan ke jenis).
Mike Stoddart
Karena cara kerja refleksi, akses ke manajemen matahari. * Tidak diperlukan. Lakukan saja getDeclaredMethodpada Objek yang dikembalikan oleh jvm.get(runtime).
Andrew T Finnell
36

Coba Sigar . API yang sangat luas. Lisensi 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
sumber
6
Anda akan memiliki lebih banyak upvotes jika Anda menjelaskan cara menggunakan SIGAR untuk ini
Brad Mace
1
Tautan rusak. Anda memberi contoh bagaimana menggunakan ini, tetapi apakah ada ketergantungan pakar untuk itu?
Jules
github.com/hyperic/sigar tampaknya merupakan lokasi yang dapat digunakan; ini juga menunjukkan bahwa itu kode asli dengan Java binding, yang mungkin membuatnya kurang dari solusi untuk banyak konteks.
Zastai
26

Metode berikut mencoba untuk mengekstrak PID dari 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;
}

Panggil saja getProcessId("<PID>"), misalnya.

Martin
sumber
6
VisualVM menggunakan kode serupa untuk mendapatkan PID mandiri, periksa com.sun.tools.visualvm.application.jvm.Jvm # ApplicationSupport # createCurrentApplication (). Mereka adalah para ahli, jadi sepertinya solusi lintas platform yang dapat diandalkan.
Espinosa
@Espinosa VisualVM hanya mendukung berjalan pada OpenJDK / Oracle / GraalVM JVM (pada 2020). Ini berarti mereka dapat membuat asumsi yang sesuai. Jika Anda melakukan hal yang sama, Anda menjadi tergantung pada vendor dan kode Anda dapat rusak jika dijalankan misalnya J9.
Thorbjørn Ravn Andersen
24

Untuk JVM yang lebih lama, di 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
sumber
52
Jika Anda akan melakukan itu, maka lakukan saja int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());. Mengapa girasi ekstra?
David Citron
2
Bagi yang penasaran, trik dalam komentar @vid benar-benar berfungsi. Adakah yang bisa mengatakan mengapa? Semacam sihir dalam getCanonicalFile()metode yang mengubah "diri" menjadi PID?
GreenGiant
7
getCanonicalFile () menyelesaikan symlink / proc / self / -> / proc / 1234, coba ls -al / proc / self
Michael
2
@DavidCitron +1! Anda benar, saya tidak berpikir di getCanonicalFile :-)
ggrandes
18

Anda dapat melihat proyek saya: JavaSysMon di GitHub. Ini memberikan id proses dan banyak hal lainnya (penggunaan CPU, penggunaan memori) lintas-platform (saat ini Windows, Mac OSX, Linux dan Solaris)

Jez Humble
sumber
Apakah mungkin untuk mendapatkan PID Proses dimulai oleh Java? Atau mulai Proses "jalan Anda" untuk memperbaiki masalah ini?
Panayotis
17

Karena Java 9 ada metode Process.getPid () yang mengembalikan ID asli suatu proses:

public abstract class Process {

    ...

    public long getPid();
}

Untuk mendapatkan ID proses dari proses Java saat ini, seseorang dapat menggunakan ProcessHandleantarmuka:

System.out.println(ProcessHandle.current().pid());
ZhekaKozlov
sumber
2
Bisakah Anda menjelaskan cara mendapatkan contoh Proses untuk proses yang sedang berjalan di Java 9?
Augusto
Jawaban yang bagus untuk menggunakan Java 9 pada 2016! Bisakah Anda menambahkan tautan ke javadocs? terima kasih
crazyGuy
8

Dalam Scala:

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

Ini akan memberi Anda solusi pada sistem Unix sampai Java 9 akan dirilis. (Saya tahu, pertanyaannya adalah tentang Java, tetapi karena tidak ada pertanyaan yang setara untuk Scala, saya ingin meninggalkan ini untuk pengguna Scala yang mungkin tersandung ke pertanyaan yang sama.)

Florin T.
sumber
7

Untuk kelengkapan ada pembungkus di Spring Boot untuk

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

larutan. Jika bilangan bulat diperlukan, maka ini dapat disimpulkan hingga satu-baris:

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

Jika seseorang sudah menggunakan boot Spring, ia mungkin menggunakan org.springframework.boot.ApplicationPid

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

Metode toString () mencetak pid atau '???'.

Peringatan menggunakan ManagementFactory sudah dibahas dalam jawaban lain.

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

Yang terbaru yang saya temukan adalah bahwa ada sistem properti yang disebut sun.java.launcher.pidyang tersedia setidaknya di linux. Rencana saya adalah menggunakan itu dan jika tidak ditemukan menggunakan JMX bean.

Jared
sumber
Dalam kasus saya dengan Ubuntu 16.04 dan Java 8, ia mengembalikan null
david.perez
4

Tergantung di mana Anda mencari informasi.

Jika Anda mencari informasi dari konsol Anda dapat menggunakan perintah jps. Perintah memberikan output mirip dengan perintah ps Unix dan datang dengan JDK sejak saya percaya 1,5

Jika Anda melihat dari proses, RuntimeMXBean (seperti yang dikatakan oleh Wouter Coekaerts) mungkin merupakan pilihan terbaik Anda. Output dari getName () pada Windows menggunakan Sun JDK 1.6 u7 adalah dalam bentuk [PROCESS_ID] @ [MACHINE_NAME]. Namun Anda bisa mencoba mengeksekusi jps dan mem-parsing hasil dari itu:

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

Jika dijalankan tanpa opsi, output harus menjadi id proses diikuti dengan nama.

Ryan P
sumber
1
Alat JPS menggunakan perpustakaan jvmstat, bagian dari tools.jar. Lihat contoh saya atau lihat kode sumber JPS: grepcode.com/file_/repository.grepcode.com/java/root/jdk/… . Tidak perlu memanggil JPS sebagai proses eksternal, gunakan perpustakaan jvmstat secara langsung.
Espinosa
4

Ini adalah kode JConsole, dan berpotensi menggunakan jps dan VisualVM. Ini menggunakan kelas dari sun.jvmstat.monitor.*paket, dari 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;
    }
}

Ada beberapa tangkapan:

  • Itu tool.jar adalah perpustakaan yang didistribusikan dengan Oracle JDK tetapi tidak JRE!
  • Kamu tidak bisa mendapatkan tool.jar dari repo Maven; mengkonfigurasinya dengan Maven agak sulit
  • Itu tool.jar mungkin mengandung tergantung platform (asli?) Kode sehingga tidak mudah didistribusikan
  • Ini berjalan dengan asumsi bahwa semua aplikasi JVM (lokal) "dipantau". Sepertinya dari Java 6 semua aplikasi pada umumnya (kecuali jika Anda secara aktif mengonfigurasi yang berlawanan)
  • Mungkin hanya berfungsi untuk Java 6+
  • Eclipse tidak menerbitkan kelas utama, jadi Anda tidak akan mendapatkan Eclipse PID dengan mudah Bug di MonitoredVmUtil?

UPDATE: Saya baru saja memeriksa ulang bahwa JPS menggunakan cara ini, yaitu perpustakaan Jvmstat (bagian dari tool.jar). Jadi tidak perlu memanggil JPS sebagai proses eksternal, panggil perpustakaan Jvmstat secara langsung seperti contoh saya. Anda juga dapat memperoleh daftar semua runnin JVM di localhost dengan cara ini. Lihat kode sumber JPS :

Espinosa
sumber
jika mainClass.getName () tidak berfungsi coba gunakan mainClass.getCanonicalName ();
Narendra Parmar
3

Saya menambahkan ini, ke solusi lain.

dengan Java 10, untuk mendapatkannya process id

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

Anda dapat mencoba getpid()di JNR-Posix .

Memiliki pembungkus Windows POSIX yang memanggil getpid () dari libc.

kervin
sumber
1

Saya tahu ini adalah utas lama, tetapi saya ingin memanggil bahwa API untuk mendapatkan PID (serta manipulasi lain dari proses Java saat runtime) sedang ditambahkan ke kelas Proses di JDK 9: http: // openjdk. java.net/jeps/102

Brandon Heck
sumber
9
Wow, itu cepat. Hanya butuh sekitar tujuh tahun! 😃
Dmitry Shechtman
1

Saya menemukan solusi yang mungkin sedikit kasus tepi dan saya tidak mencobanya pada OS lain selain Windows 10, tapi saya pikir itu layak diperhatikan.

Jika Anda menemukan diri Anda bekerja dengan J2V8 dan nodejs, Anda dapat menjalankan fungsi javascript sederhana dan mengembalikan Anda pid dari proses java.

Berikut ini sebuah contoh:

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

Inilah solusi saya:

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;
    }
Data Partial
sumber
Ini tidak memeriksa apakah pid digunakan, tetapi apakah pid sama dengan proses saat ini pid
c0der
Apa solusi yang tepat sehingga saya dapat memperbarui kode saya?
PartialData
-6

Inilah yang saya gunakan ketika saya memiliki persyaratan yang sama. Ini menentukan PID proses Java dengan benar. Biarkan kode java Anda menelurkan server pada nomor port yang telah ditentukan sebelumnya dan kemudian jalankan perintah OS untuk mengetahui mendengarkan PID pada port tersebut. Untuk Linux

netstat -tupln | grep portNumber
Subhash
sumber
3
Jika Anda memanggil skrip shell, Anda sebaiknya melakukan sesuatu yang lebih sederhana seperti bash -c 'echo $PPID'atau / proc jawaban di atas
Kaya