You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何监控Java Process API启动的Linux子进程的CPU与内存占用?

监控通过ProcessBuilder启动的Linux子进程资源占用

在Linux环境下,你可以通过以下几种方式监控ProcessBuilder.start()启动的子进程的CPU、内存等资源占用:

方法一:结合系统命令与Java Process API

利用Linux自带的进程查询命令(如ps、pidstat),结合Java获取到的子进程PID来查询资源数据:

步骤:

  1. 获取子进程PID

    • Java 9及以上:直接调用Process.pid()方法获取PID。
    • Java 8及以下:通过反射解析Process类的内部字段,或从process.toString()的输出中提取(Linux下格式通常为Process[pid=xxx])。
  2. 执行系统命令查询资源
    常用命令示例:

    • 查看CPU使用率与常驻内存:ps -p <pid> -o %cpu,rss
    • 更详细的CPU统计:pidstat -p <pid> 1 1
    • 实时资源快照:top -b -n 1 -p <pid>
  3. 定时监控多进程
    使用ScheduledExecutorService定期对每个存活的子进程执行查询,避免阻塞主线程。

代码示例(Java 9+):

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class MultiProcessMonitor {
    public static void main(String[] args) throws IOException {
        // 启动多个子进程
        List<Process> processes = new ArrayList<>();
        processes.add(new ProcessBuilder("sleep", "120").start());
        processes.add(new ProcessBuilder("dd", "if=/dev/zero", "of=/dev/null").start());

        ScheduledExecutorService monitorExecutor = Executors.newScheduledThreadPool(1);
        // 每2秒执行一次监控
        monitorExecutor.scheduleAtFixedRate(() -> {
            for (Process proc : processes) {
                if (!proc.isAlive()) {
                    System.out.printf("进程PID %d 已退出%n", proc.pid());
                    continue;
                }
                long pid = proc.pid();
                try {
                    // 执行ps命令获取CPU和内存数据
                    Process psProc = new ProcessBuilder("ps", "-p", String.valueOf(pid), "-o", "%cpu,rss").start();
                    try (BufferedReader reader = new BufferedReader(new InputStreamReader(psProc.getInputStream()))) {
                        reader.readLine(); // 跳过表头行
                        String dataLine = reader.readLine();
                        if (dataLine != null) {
                            String[] parts = dataLine.trim().split("\\s+");
                            String cpuUsage = parts[0];
                            String rssMemory = parts[1] + " KB";
                            System.out.printf("进程PID %d: CPU使用率 %s%%, 常驻内存 %s%n", pid, cpuUsage, rssMemory);
                        }
                    }
                    psProc.waitFor();
                } catch (IOException | InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 0, 2, TimeUnit.SECONDS);
    }
}

方法二:直接读取Linux /proc文件系统

Linux下每个进程的资源信息都以文件形式存储在/proc/<pid>/目录下,直接读取这些文件无需执行外部命令,效率更高:

常用的proc文件:

  • /proc/<pid>/stat:包含进程的CPU时间、状态等信息
  • /proc/<pid>/status:包含内存占用、线程数等可读格式的数据
  • /proc/<pid>/statm:内存使用的统计信息(单位为页)

代码示例(读取常驻内存):

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ProcFileMultiMonitor {
    public static void main(String[] args) throws IOException {
        List<Process> processes = new ArrayList<>();
        processes.add(new ProcessBuilder("sleep", "120").start());
        processes.add(new ProcessBuilder("dd", "if=/dev/zero", "of=/dev/null").start());

        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        executor.scheduleAtFixedRate(() -> {
            for (Process proc : processes) {
                if (!proc.isAlive()) {
                    System.out.printf("进程PID %d 已退出%n", proc.pid());
                    continue;
                }
                long pid = proc.pid();
                String statusPath = "/proc/" + pid + "/status";
                try {
                    for (String line : Files.readAllLines(Paths.get(statusPath))) {
                        if (line.startsWith("VmRSS:")) {
                            String[] parts = line.trim().split("\\s+");
                            String memoryInfo = parts[1] + " " + parts[2];
                            System.out.printf("进程PID %d: 常驻内存 %s%n", pid, memoryInfo);
                            break;
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }, 0, 2, TimeUnit.SECONDS);
    }
}

精确计算CPU使用率

若需要精确计算CPU使用率,需两次读取/proc/<pid>/stat和/proc/stat(系统总CPU时间),通过时间差值计算:

  1. 读取/proc/<pid>/stat的第14(utime)、15(stime)字段(进程用户态/内核态CPU时间,单位为jiffies)
  2. 读取/proc/stat第一行的所有数值之和(系统总CPU时间)
  3. 间隔一段时间后重复读取,计算差值:
    进程CPU总时间差 = (utime2 + stime2) - (utime1 + stime1)
    系统总CPU时间差 = systemCpuTime2 - systemCpuTime1
    CPU使用率 = (进程CPU总时间差 / 系统总CPU时间差) * 100 * 可用CPU核数
    

注意事项

  • 子进程退出后,对应的/proc/<pid>/目录会被删除,查询前需先调用process.isAlive()判断状态
  • Java 8及以下获取PID的反射实现:
    private static long getPidForJava8(Process process) throws NoSuchFieldException, IllegalAccessException {
        Field pidField = process.getClass().getDeclaredField("pid");
        pidField.setAccessible(true);
        return pidField.getLong(process);
    }
    
  • 不同Linux发行版的proc文件格式或命令输出可能略有差异,需做好兼容处理

内容的提问来源于stack exchange,提问作者Thomas Bratt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 00:10:35