Làm thế nào một chương trình Java có thể có ID tiến trình riêng của nó?


341

Làm cách nào để có id của quy trình Java của tôi?

Tôi biết có một số hack phụ thuộc vào nền tảng, nhưng tôi thích một giải pháp chung hơn.



4
Điều này có nghĩa là được sửa trong JDK9. openjdk.java.net/jeps/102
Andrew

Câu trả lời:


322

Không tồn tại cách độc lập với nền tảng nào có thể được đảm bảo để hoạt động trong tất cả các triển khai jvm. ManagementFactory.getRuntimeMXBean().getName()trông giống như giải pháp tốt nhất (gần nhất) Nó ngắn và có thể hoạt động trong mọi triển khai được sử dụng rộng rãi.

Trên linux + windows, nó trả về một giá trị như 12345@hostname( 12345là id quá trình). Hãy coi chừng rằng theo các tài liệu , không có gì đảm bảo về giá trị này:

Trả về tên đại diện cho máy ảo Java đang chạy. Chuỗi tên được trả về có thể là bất kỳ chuỗi tùy ý nào và việc triển khai máy ảo Java có thể chọn để nhúng thông tin hữu ích dành riêng cho nền tảng trong chuỗi tên được trả về. Mỗi máy ảo đang chạy có thể có một tên khác nhau.

Trong Java 9 , API quy trình mới có thể được sử dụng:

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

2
Giải pháp này thực sự rất mong manh. Xem câu trả lời về Hyperic Sigar dưới đây.
Michael Klishin

pid đó là tốt để viết trên một tập tin khóa như stackoverflow.com/a/9020391/1422630
Aquarius Power

111

Bạn có thể sử dụng JNA . Thật không may, chưa có API JNA chung để nhận ID quy trình hiện tại, nhưng mỗi nền tảng khá đơn giản:

các cửa sổ

Hãy chắc chắn rằng bạn có jna-platform.jar:

int pid = Kernel32.INSTANCE.GetCurrentProcessId();

Unix

Khai báo:

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

Sau đó:

int pid = CLibrary.INSTANCE.getpid();

Java 9

Trong Java 9, API quy trình mới có thể được sử dụng để lấy ID quy trình hiện tại. Trước tiên, bạn lấy một điều khiển cho quy trình hiện tại, sau đó truy vấn PID:

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

4
+1 Nhưng tôi e rằng trong một môi trường bị hạn chế bảo mật, nó không hoạt động (tomcat, WebLogic, v.v.).
ATorras

Các getpid()giải pháp cũng làm việc cho OS X, với một cuộc gọi JNA vào thư viện "hệ thống".
Daniel Widdis

Tuy nhiên, tôi nhận được error: cannot find symbolcho jdk9
skytree

56

Đây là một phương thức cửa sau có thể không hoạt động với tất cả các máy ảo nhưng sẽ hoạt động trên cả linux và windows ( ví dụ ban đầu ở đây ):

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
rất hay, chỉ cần xác minh rằng nó hoạt động cả trên JRockit cũng như các JVM Hotspot.
Drupad Panchal

1
Cách giải quyết tốt đẹp. Tôi sẽ giả định rằng có một lý do chính đáng tại sao phương pháp này (và các phương pháp khác trong lớp) không công khai và dễ dàng truy cập, và tôi tò mò muốn biết nó là gì.
Fragorl

2
Nhóm Java Java đã thông báo rằng họ có ý định ẩn tất cả các gói không phải java (tức là mặt trời. *) Tôi tin rằng bắt đầu với Java 10 (dự kiến ​​vào năm 2018 - có thể là Java 9). Nếu bạn có một triển khai tương tự như ở trên, bạn có thể muốn tìm ra một giải pháp thay thế để mã của bạn không bị hỏng.
hfontanez

Không hoạt động đối với tôi vì mặt trời. * Các gói có thể bị xóa hoặc không thể truy cập (VMMan quản lý không thể được giải quyết thành một loại).
Mike Stoddart

Do cách phản chiếu hoạt động, không cần truy cập vào sun.man quản lý. *. Chỉ cần thực hiện getDeclaredMethodtrên Object trả về jvm.get(runtime).
Andrew T Finnell

36

Hãy thử Sigar . API rất rộng. Giấy phép 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
Bạn sẽ có nhiều ưu đãi hơn nếu bạn giải thích cách sử dụng SIGAR cho việc này
Brad Mace

1
Liên kết bị hỏng. Bạn đưa ra một ví dụ về cách sử dụng cái này, nhưng có phụ thuộc maven cho nó không?
Jules

github.com/hyperic/sigar dường như là một địa điểm có thể sử dụng được; điều này cũng cho thấy đó là mã riêng với các ràng buộc Java, điều này có thể làm cho nó trở thành một giải pháp cho nhiều bối cảnh.
Zastai

26

Phương pháp sau đây cố gắng trích xuất PID từ 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;
}

Chỉ cần gọi getProcessId("<PID>"), ví dụ.


6
VisualVM sử dụng mã tương tự để có được tự PID, kiểm tra com.sun.tools.visualvm.application.jvm.Jvm # ApplicationSupport # createCienApplication (). Họ là những chuyên gia, vì vậy có vẻ như đáng tin cậy, giải pháp đa nền tảng.
Espinosa

@Espinosa VisualVM chỉ hỗ trợ chạy trên JVM OpenJDK / Oracle / GraalVM (tính đến năm 2020). Điều này có nghĩa là họ có thể đưa ra các giả định phù hợp. Nếu bạn làm như vậy, bạn trở thành người phụ thuộc của nhà cung cấp và mã của bạn có thể bị hỏng nếu chạy trên ví dụ J9.
Thorbjørn Ravn Andersen

24

Đối với JVM cũ hơn, trong 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
Nếu bạn sẽ làm điều đó, sau đó chỉ cần làm int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());. Tại sao các con quay thêm?
David Citron

2
Đối với những người tò mò, mẹo trong nhận xét của @David thực sự có hiệu quả. Ai đó có thể nói tại sao không? Một số loại phép thuật trong getCanonicalFile()phương pháp chuyển đổi "bản thân" thành PID?
GreenGiant

7
getCanonicalFile () giải quyết symlink / Proc / self / -> / Proc / 1234, thử ls -al / Proc / self
Michael

2
@DavidCitron +1! bạn nói đúng, tôi không nghĩ trong getCanonicalFile :-)
ggrandes

18

Bạn có thể kiểm tra dự án của tôi: JavaSysMon trên GitHub. Nó cung cấp id quá trình và một loạt các thứ khác (sử dụng CPU, sử dụng bộ nhớ) đa nền tảng (hiện tại là Windows, Mac OSX, Linux và Solaris)


Có thể có được PID của một Quá trình được bắt đầu bởi Java không? Hoặc bắt đầu một quá trình "theo cách của bạn" để khắc phục vấn đề này?
Panayotis 17/05/2016

17

Vì Java 9 có một phương thức Process.getPid () trả về ID gốc của một quy trình:

public abstract class Process {

    ...

    public long getPid();
}

Để lấy ID tiến trình của quy trình Java hiện tại, người ta có thể sử dụng ProcessHandlegiao diện:

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

2
Bạn có thể giải thích cách lấy một cá thể Process cho quy trình đang chạy hiện tại trong Java 9 không?
Augusto

Câu trả lời tuyệt vời cho việc sử dụng Java 9 vào năm 2016! Bạn có thể thêm một liên kết đến javadocs không? cảm ơn
crazyGuy 27/2/2016

8

Trong Scala:

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

Điều này sẽ cung cấp cho bạn một cách giải quyết trên các hệ thống Unix cho đến khi Java 9 được phát hành. (Tôi biết, câu hỏi là về Java, nhưng vì không có câu hỏi tương đương với Scala, tôi muốn để lại câu hỏi này cho những người dùng Scala có thể vấp vào cùng một câu hỏi.)


7

Để hoàn thiện, có một trình bao bọc trong Spring Boot cho

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

giải pháp. Nếu một số nguyên là bắt buộc, thì điều này có thể được tóm tắt thành một lớp lót:

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

Nếu ai đó đã sử dụng Spring boot, cô ấy / anh ấy có thể sử dụng org.springframework.boot.ApplicationPid

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

Phương thức toString () in pid hoặc '???'.

Hãy cẩn thận khi sử dụng ManagementFactory đã được thảo luận trong các câu trả lời khác.


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

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

5

Cái mới nhất tôi đã tìm thấy là có một thuộc tính hệ thống được gọi sun.java.launcher.pidlà có sẵn ít nhất trên linux. Kế hoạch của tôi là sử dụng nó và nếu nó không được tìm thấy để sử dụng JMX bean.


Trong trường hợp của tôi với Ubuntu 16.04 và Java 8, nó trả về null
david.perez

4

Nó phụ thuộc vào nơi bạn đang tìm kiếm thông tin từ.

Nếu bạn đang tìm kiếm thông tin từ bàn điều khiển, bạn có thể sử dụng lệnh jps. Lệnh cho đầu ra tương tự như lệnh Unix ps và đi kèm với JDK vì tôi tin 1.5

Nếu bạn đang tìm kiếm từ quy trình thì RuntimeMXBean (như đã nói của Wouter Coekaerts) có lẽ là sự lựa chọn tốt nhất của bạn. Đầu ra từ getName () trên Windows bằng Sun JDK 1.6 u7 có dạng [PROCESS_ID] @ [MachINE_NAME]. Tuy nhiên, bạn có thể thử thực thi jps và phân tích kết quả từ đó:

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

Nếu chạy không có tùy chọn, đầu ra sẽ là id quá trình theo sau là tên.


1
Công cụ JPS sử dụng thư viện jvmstat, một phần của tools.jar. Kiểm tra ví dụ của tôi hoặc xem mã nguồn JPS: grepcode.com/file_/reposective.grepcode.com/java/root/jdk/ . Không cần phải gọi JPS là quy trình bên ngoài, hãy sử dụng thư viện jvmstat trực tiếp.
Espinosa

4

Đây là mã JConsole, và có khả năng sử dụng jps và VisualVM. Nó sử dụng các lớp từ sun.jvmstat.monitor.*gói, từ 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;
    }
}

Có một vài lưu ý:

  • Đây tool.jarlà một thư viện được phân phối với Oracle JDK nhưng không phải JRE!
  • Bạn không thể lấy tool.jartừ Maven repo; cấu hình nó với Maven là một chút khó khăn
  • tool.jarlẽ chứa mã phụ thuộc nền tảng (bản địa?) Nên không dễ phân phối
  • Nó chạy theo giả định rằng tất cả các ứng dụng JVM (cục bộ) đang chạy đều "có thể theo dõi". Có vẻ như từ Java 6, tất cả các ứng dụng thường là (trừ khi bạn chủ động cấu hình ngược lại)
  • Nó có lẽ chỉ hoạt động cho Java 6+
  • Eclipse không xuất bản lớp chính, vì vậy bạn sẽ không dễ dàng gặp lỗi Eclipse Eclipse trong MonitoredVmUtil?

CẬP NHẬT: Tôi vừa kiểm tra hai lần rằng JPS sử dụng theo cách này, đó là thư viện Jvmstat (một phần của tool.jar). Vì vậy, không cần phải gọi JPS là quy trình bên ngoài, hãy gọi thư viện Jvmstat trực tiếp như ví dụ của tôi cho thấy. Bạn cũng có thể lấy danh sách tất cả các JVM chạy trên localhost theo cách này. Xem mã nguồn JPS :


nếu mainClass.getName () không hoạt động, hãy thử sử dụng mainClass.getCanonicalName ();
Narendra Parmar

3

Tôi đang thêm điều này, cho các giải pháp khác.

với Java 10, để có được process id

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

2

Dựa trên câu trả lời của Ashwin Jayaprakash (+1) về Apache 2.0 được cấp phép SIGAR, đây là cách tôi sử dụng nó để chỉ nhận được PID của quy trình hiện tại:

import org.hyperic.sigar.Sigar;

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

Mặc dù nó không hoạt động trên tất cả các nền tảng, nhưng nó hoạt động trên Linux, Windows, OS X và các nền tảng Unix khác nhau như được liệt kê ở đây .


1

Bạn có thể thử getpid()trong JNR-Posix .

Nó có một trình bao bọc Windows POSIX gọi getpid () khỏi libc.



1

Tôi đã tìm thấy một giải pháp có thể là một chút của một trường hợp cạnh và tôi đã không thử nó trên hệ điều hành khác ngoài Windows 10, nhưng tôi nghĩ rằng nó đáng chú ý.

Nếu bạn thấy mình làm việc với J2V8 và nodejs, bạn có thể chạy một hàm javascript đơn giản trả về cho bạn bản chính của quy trình java.

Đây là một ví dụ:

public static void main(String[] args) {
    NodeJS nodeJS = NodeJS.createNodeJS();
    int pid = nodeJS.getRuntime().executeIntegerScript("process.pid;\n");
    System.out.println(pid);
    nodeJS.release();
}

-1

Đây là giải pháp của tôi:

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

Điều này không kiểm tra xem pid có được sử dụng hay không, nhưng nếu pid bằng với pid của quy trình hiện tại
c0der

Giải pháp chính xác để tôi có thể cập nhật mã của mình là gì?
PartialData

-6

Đây là những gì tôi đã sử dụng khi tôi có yêu cầu tương tự. Điều này xác định chính xác PID của quy trình Java. Hãy để mã java của bạn sinh ra một máy chủ trên một số cổng được xác định trước và sau đó thực thi các lệnh OS để tìm ra cách nghe PID trên cổng. Dành cho Linux

netstat -tupln | grep portNumber

3
Nếu bạn đang gọi các kịch bản shell, bạn cũng có thể thực hiện một số thao tác đơn giản hơn bash -c 'echo $PPID'hoặc câu trả lời
Rich
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.