โปรแกรม Java สามารถรับ ID กระบวนการของตัวเองได้อย่างไร


341

ฉันจะรับรหัสกระบวนการ Java ของฉันได้อย่างไร

ฉันรู้ว่ามีแฮ็กขึ้นอยู่กับหลายแพลตฟอร์ม แต่ฉันต้องการโซลูชันที่กว้างกว่า



4
สิ่งนี้มีไว้เพื่อแก้ไขใน JDK9 openjdk.java.net/jeps/102
Andrew

วิธีที่จะทำใน Java SE10
mrsrinivas

คำตอบ:


322

ไม่มีวิธีที่ไม่ขึ้นกับแพลตฟอร์มที่สามารถรับประกันได้ว่าจะทำงานในการปรับใช้ jvm ทั้งหมด ManagementFactory.getRuntimeMXBean().getName()ดูเหมือนโซลูชันที่ใกล้เคียงที่สุด มันสั้นและอาจใช้ได้กับทุกการใช้งานอย่างกว้างขวาง

บน linux + windows จะส่งคืนค่าเช่น12345@hostname( 12345เป็น id กระบวนการ) ระวังว่าตามเอกสารไม่มีการรับประกันเกี่ยวกับค่านี้:

ส่งคืนชื่อที่แสดงถึงเครื่องเสมือน Java ที่รันอยู่ สตริงชื่อที่ส่งคืนสามารถเป็นสตริงใดก็ได้และการใช้งานเครื่องเสมือน Java สามารถเลือกที่จะฝังข้อมูลที่เป็นประโยชน์เฉพาะแพลตฟอร์มในสตริงชื่อที่ส่งคืน เครื่องเสมือนที่ใช้งานอยู่แต่ละเครื่องอาจมีชื่อแตกต่างกัน

ใน Java 9 API กระบวนการใหม่สามารถใช้ได้:

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

2
วิธีนี้มีความเปราะบางจริงๆ ดูคำตอบเกี่ยวกับ Hyperic Sigar ด้านล่าง
Michael Klishin

pid นั้นดีเขียนไฟล์ล็อคเป็นstackoverflow.com/a/9020391/1422630
Aquarius Power

111

คุณสามารถใช้JNA น่าเสียดายที่ไม่มี JNA API ทั่วไปในการรับ ID กระบวนการปัจจุบัน แต่แต่ละแพลตฟอร์มนั้นค่อนข้างง่าย:

ของ 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 กระบวนการใหม่สามารถใช้เพื่อรับ ID กระบวนการปัจจุบัน ก่อนอื่นให้คุณจับที่จับไปยังกระบวนการปัจจุบันจากนั้นสอบถาม PID:

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

4
+1 แต่ฉันเกรงว่าในสภาพแวดล้อมที่ จำกัด การรักษาความปลอดภัยมันไม่ควรทำงาน (Tomcat, WebLogic ฯลฯ )
ATorras

getpid()วิธีการแก้ปัญหายังทำงานสำหรับ OS X กับโทร JNA กับ "ระบบ" ห้องสมุด
Daniel Widdis

อย่างไรก็ตามฉันได้รับerror: cannot find symbolสำหรับ jdk9
กายทรี

56

นี่เป็นวิธีแบ็คดอร์ซึ่งอาจใช้ไม่ได้กับ VMs ทั้งหมด แต่ควรใช้ได้กับทั้ง 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);

2
ดีมากเพิ่งยืนยันว่ามันใช้ได้ทั้งกับ JRockit และ JVM ที่เป็น Hotspot
Drupad Panchal

1
วิธีแก้ปัญหาที่ดี ฉันจะถือว่ามีเหตุผลที่ดีว่าทำไมวิธีนี้ (และคนอื่น ๆ ในชั้นเรียน) ไม่เปิดเผยต่อสาธารณะและเข้าถึงได้ง่ายและฉันอยากรู้ว่ามันคืออะไร
Fragorl

2
ทีมงาน Oracle Java ได้ประกาศว่าพวกเขาตั้งใจที่จะซ่อนแพ็คเกจที่ไม่ใช่จาวาทั้งหมด (เช่น sun. *) ฉันเชื่อว่าเริ่มต้นด้วย Java 10 (กำหนดไว้สำหรับปี 2018 - อาจเป็น Java 9) หากคุณมีการนำไปใช้ที่คล้ายกันเหมือนกับที่กล่าวมาข้างต้นคุณอาจต้องการหาทางเลือกอื่นเพื่อไม่ให้โค้ดของคุณแตก
hfontanez

ใช้งานไม่ได้กับฉันทั้งดวงอาทิตย์แพคเกจอาจถูกลบออกหรือไม่สามารถเข้าถึงได้ (VMManagement ไม่สามารถแก้ไขเป็นประเภท)
Mike Stoddart

เนื่องจากวิธีการสะท้อนการทำงานจึงไม่จำเป็นต้องเข้าถึงการจัดการด้วยแสงแดด * เพียงแค่ดำเนินการเกี่ยวกับวัตถุที่ส่งกลับโดยgetDeclaredMethod jvm.get(runtime)
Andrew T Finnell

36

ลองSigar 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();
}

6
คุณจะมี upvotes มากขึ้นถ้าคุณอธิบายวิธีใช้ SIGAR สำหรับเรื่องนี้
Brad Mace

1
ลิงก์เสีย คุณยกตัวอย่างวิธีใช้สิ่งนี้ แต่มีการพึ่งพาอาศัย maven หรือไม่?
Jules

github.com/hyperic/sigarดูเหมือนว่าเป็นตำแหน่งที่ใช้งานได้ สิ่งนี้ยังแสดงให้เห็นว่ามันเป็นรหัสเนทีฟที่มีการเชื่อมโยง Java ซึ่งอาจทำให้โซลูชันน้อยลงสำหรับบริบทจำนวนมาก
Zastai

26

วิธีการต่อไปนี้พยายามดึง 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>")ตัวอย่างเช่น


6
VisualVM ใช้รหัสที่คล้ายกันเพื่อรับ PID ด้วยตนเองตรวจสอบ com.sun.tools.visualvm.application.jvm.Jvm # ApplicationSupport # createCurrentApplication () พวกเขาเป็นผู้เชี่ยวชาญจึงดูเหมือนโซลูชันข้ามแพลตฟอร์มที่เชื่อถือได้
Espinosa

@Espinosa VisualVM รองรับการทำงานบน OpenJDK / Oracle / GraalVM JVM (จนถึงปี 2020) นี่หมายความว่าพวกเขาสามารถตั้งสมมติฐานได้ หากคุณทำเช่นนี้คุณจะขึ้นอยู่กับผู้ขายและรหัสของคุณอาจเสียหายหากทำงานเช่น J9
Thorbjørn Ravn Andersen

24

สำหรับ 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";
}

52
int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());หากคุณกำลังจะไปทำอย่างนั้นแล้วก็ทำ ทำไมต้อง gyrations เพิ่มเติม?
David Citron

2
สำหรับความอยากรู้อยากเห็นกลเม็ดในความคิดเห็นของ @David ใช้งานได้จริง บางคนสามารถพูดว่าทำไม เวทมนต์บางอย่างในgetCanonicalFile()วิธีการที่แปลง "ตนเอง" เป็น PID?
GreenGiant

7
getCanonicalFile () แก้ไข symlink / proc / self / -> / proc / 1234 ลอง ls -al / proc / self
Michael

2
@DavidCitron +1! คุณพูดถูกฉันไม่ได้คิดว่าใน getCanonicalFile :-)
ggrandes

18

คุณสามารถตรวจสอบโครงการของฉัน: JavaSysMonบน GitHub มันมี id กระบวนการและสิ่งอื่น ๆ (การใช้งาน CPU, การใช้หน่วยความจำ) ข้ามแพลตฟอร์ม (ปัจจุบันคือ Windows, Mac OSX, Linux และ Solaris)


เป็นไปได้หรือไม่ที่จะทำให้ PID ของกระบวนการเริ่มต้นโดย Java หรือเริ่มกระบวนการ "วิธีการของคุณ" เพื่อแก้ไขปัญหานี้?
Panayotis

17

ตั้งแต่ Java 9 มีเมธอด Process.getPid () ซึ่งส่งคืนเนทีฟไอดีของกระบวนการ:

public abstract class Process {

    ...

    public long getPid();
}

เพื่อรับ ID กระบวนการของกระบวนการ Java ปัจจุบันสามารถใช้ProcessHandleอินเตอร์เฟส:

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

2
คุณสามารถอธิบายวิธีรับอินสแตนซ์กระบวนการสำหรับกระบวนการทำงานปัจจุบันใน Java 9 ได้หรือไม่
Augusto

คำตอบที่ยอดเยี่ยมสำหรับการใช้ Java 9 ในปี 2016! คุณสามารถเพิ่มลิงค์ไปยัง javadocs ได้หรือไม่? ขอบคุณ
crazyGuy

8

ในสกาล่า:

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

สิ่งนี้จะให้วิธีแก้ปัญหาแก่คุณในระบบ Unix จนกว่า Java 9 จะวางจำหน่าย (ฉันรู้ว่าคำถามเกี่ยวกับ Java แต่เนื่องจากไม่มีคำถามเทียบเท่าสำหรับ Scala ฉันต้องการปล่อยให้ผู้ใช้ Scala ที่อาจสะดุดในคำถามเดียวกัน)


7

เพื่อความสมบูรณ์มีเสื้อคลุมในSpring Bootสำหรับ

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

สารละลาย. หากจำเป็นต้องใช้จำนวนเต็มค่านี้สามารถสรุปได้ถึงหนึ่งซับ:

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

หากมีใครใช้ Spring boot แล้วเขา / เธออาจใช้org.springframework.boot.ApplicationPid

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

เมธอด toString () พิมพ์ pid หรือ '???'

คำเตือนที่ใช้ ManagementFactory จะถูกกล่าวถึงในคำตอบอื่น ๆ แล้ว



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

ล่าสุดที่ฉันพบคือมีคุณสมบัติของระบบที่เรียกsun.java.launcher.pidว่ามีอย่างน้อยบน linux JMX beanแผนของฉันคือการใช้ที่และหากยังไม่พบว่ามีการใช้


ในกรณีของฉันกับ Ubuntu 16.04 และ Java 8 มันคืนค่า null
david.perez

4

ขึ้นอยู่กับว่าคุณต้องการข้อมูลจากที่ไหน

หากคุณกำลังมองหาข้อมูลจากคอนโซลคุณสามารถใช้คำสั่ง 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);

หากรันโดยไม่มีตัวเลือกเอาต์พุตควรเป็น id กระบวนการตามด้วยชื่อ


1
เครื่องมือ JPS ใช้ไลบรารี jvmstat ซึ่งเป็นส่วนหนึ่งของ tools.jar ตรวจสอบตัวอย่างของฉันหรือดูซอร์สโค้ด JPS: grepcode.com/file_/repository.grepcode.com/java/root/jdk/ … ไม่จำเป็นต้องเรียก JPS เป็นกระบวนการภายนอกให้ใช้ไลบรารี jvmstat โดยตรง
Espinosa

4

นี่คือรหัส 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 repo; กำหนดค่าด้วย Maven ค่อนข้างยุ่งยาก
  • tool.jarอาจจะมีขึ้นอยู่กับแพลตฟอร์ม native (?) รหัสดังนั้นจึงไม่สามารถแจกจ่ายได้อย่างง่ายดาย
  • มันทำงานภายใต้สมมติฐานว่าแอป JVM ที่ทำงานอยู่ในเครื่องทั้งหมดนั้นสามารถตรวจสอบได้ ดูเหมือนว่าจาก Java 6 โดยทั่วไปแล้วแอพทั้งหมด (เว้นแต่คุณจะกำหนดค่าตรงกันข้าม)
  • มันอาจใช้ได้เฉพาะกับ Java 6+ เท่านั้น
  • Eclipse ไม่ได้เผยแพร่คลาสหลักดังนั้นคุณจะไม่ได้รับ Eclipse PID ข้อผิดพลาดใน MonitoredVmUtil อย่างง่ายดายหรือไม่

UPDATE: ฉันเพิ่งตรวจสอบสองครั้งว่า JPS ใช้วิธีนี้นั่นคือไลบรารี Jvmstat (ส่วนหนึ่งของ tool.jar) ดังนั้นไม่จำเป็นต้องเรียก JPS เป็นกระบวนการภายนอกให้เรียกไลบรารี Jvmstat โดยตรงตามตัวอย่างที่แสดง คุณยังสามารถรับรายชื่อของ JVMs runnin ทั้งหมดบน localhost ด้วยวิธีนี้ ดูซอร์สโค้ด JPS :


ถ้า mainClass.getName () ไม่ทำงานลองใช้ mainClass.getCanonicalName ();
Narendra Parmar

3

ฉันกำลังเพิ่มสิ่งนี้ลงในโซลูชันอื่น

ด้วย Java 10, เพื่อรับ process id

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

2

จากคำตอบของ Ashwin Jayaprakash (+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 ต่าง ๆ ตามที่ระบุไว้ที่นี่


1

คุณสามารถลองgetpid()ในJNR-Posix

มันมี wrapper ของ Windows POSIX ที่เรียกใช้ getpid () จาก libc


1

ฉันรู้ว่านี่เป็นเธรดเก่า แต่ฉันต้องการเรียกใช้ API นั้นเพื่อรับ PID (รวมถึงการจัดการอื่น ๆ ของกระบวนการ Java ตอนรันไทม์) กำลังถูกเพิ่มเข้าในคลาส Process ใน JDK 9: http: // openjdk java.net/jeps/102


9
ว้าวนั่นเร็วมาก ใช้เวลาประมาณเจ็ดปี! 😃
Dmitry Shechtman

1

ฉันพบวิธีแก้ปัญหาที่อาจเป็นเรื่องเล็กน้อยและฉันไม่ได้ลองบนระบบปฏิบัติการอื่นที่ไม่ใช่ Windows 10 แต่ฉันคิดว่ามันน่าสังเกต

หากคุณพบว่าตัวเองทำงานกับJ2V8และ nodejs คุณสามารถเรียกใช้ฟังก์ชั่นจาวาสคริปต์อย่างง่ายซึ่งจะคืนค่า 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();
}

-1

นี่คือทางออกของฉัน:

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 หรือไม่ แต่ถ้า pid เท่ากับกระบวนการปัจจุบัน pid
c0der

ทางออกที่ถูกต้องคืออะไรเพื่อให้ฉันสามารถอัปเดตรหัสของฉันได้
PartialData

-6

นี่คือสิ่งที่ฉันใช้เมื่อฉันมีความต้องการที่คล้ายกัน สิ่งนี้กำหนด PID ของกระบวนการ Java อย่างถูกต้อง ปล่อยให้รหัส java ของคุณวางไข่เซิร์ฟเวอร์ที่หมายเลขพอร์ตที่กำหนดไว้ล่วงหน้าจากนั้นดำเนินการคำสั่งระบบปฏิบัติการเพื่อค้นหาฟัง PID บนพอร์ต สำหรับ Linux

netstat -tupln | grep portNumber

3
หากคุณกำลังเรียกเชลล์สคริปคุณอาจทำสิ่งที่ง่ายกว่าbash -c 'echo $PPID'หรือคำตอบ
Rich
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.