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

使用ChromeDriver的quit()与close()后仍存在Chrome僵尸进程的问题

Chrome僵尸进程问题排查与解决(CentOS8环境)

问题描述

在CentOS8环境中运行Jar包,通过Selenium 4.7.2搭配ChromeDriver 99实现浏览器链接获取。已在代码中调用driver.close()和driver.quit(),还额外通过端口查询Chrome进程PID并尝试杀死,但程序结束后仍残留大量Chrome僵尸进程。

提供代码

...
        WebDriver driver = null;
        List<Integer> pids = new ArrayList<>();
        try {
            driver = new ChromeDriver(service, desiredCapabilities);
            Integer port = service.getUrl().getPort();
            pids = GetChromePidFromPort(port);
            log.info("detected chrome process id " + pids);
            driver.manage().window().maximize();
            driver.manage().window().setSize(new Dimension(1920, 1080));
            driver.get(url);
            log.info("drive task is done, sleep :" + duration + " ms");
            Thread.sleep(duration);
            return true;
        } catch (InterruptedException | IOException e) {
            log.info("chrome download png error");
            e.printStackTrace();
        } finally {
            if (driver != null) {
                driver.close();
                driver.quit();
                if (!pids.isEmpty()) {
                    killPid(pids);
                }
            }
        }
private static List<Integer> GetChromePidFromPort(Integer port) throws IOException {
        List<Integer> pidList = new ArrayList<>();
        Process process = Runtime.getRuntime().exec("lsof -i:" + port);
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        while ((line = reader.readLine()) != null) {
            if (line.contains("chrome")) {
                int pid = Integer.parseInt(line.split("\\s+")[1]);
                System.out.println("PID: " + pid);
                pidList.add(pid);
            }
        }
        return pidList;
    }

进程截图

Chrome僵尸进程截图

解决方法

1. 显式停止ChromeDriver服务

Selenium的ChromeDriver退出时,可能没有彻底终止底层的ChromeDriverService。需要在driver.quit()后显式停止服务:

finally {
    if (driver != null) {
        try {
            driver.quit();
        } catch (Exception e) {
            log.warn("Driver quit failed", e);
        }
    }
    // 停止ChromeDriver服务
    if (service != null && service.isRunning()) {
        try {
            service.stop();
        } catch (Exception e) {
            log.warn("ChromeDriver service stop failed", e);
        }
    }
}

2. 递归杀死所有Chrome子进程

Chrome会启动多个子进程(渲染、GPU等),这些进程不绑定同一端口,仅杀端口关联进程无法清理干净。可以通过ChromeDriver的父PID递归杀死所有子进程:

  • 先获取ChromeDriver的PID(Selenium 4+支持service.getProcessId())
  • 编写递归杀进程方法:
private static void killProcessTree(int parentPid) throws IOException, InterruptedException {
    // 获取父进程的所有子PID
    Process pgrep = Runtime.getRuntime().exec("pgrep -P " + parentPid);
    BufferedReader reader = new BufferedReader(new InputStreamReader(pgrep.getInputStream()));
    String line;
    List<Integer> childPids = new ArrayList<>();
    while ((line = reader.readLine()) != null) {
        childPids.add(Integer.parseInt(line));
    }
    reader.close();
    pgrep.waitFor();

    // 先递归杀死子进程
    for (int pid : childPids) {
        killProcessTree(pid);
    }

    // 杀死当前进程
    Runtime.getRuntime().exec("kill -9 " + parentPid);
    log.info("Killed process: " + parentPid);
}

在finally块中调用:

if (service != null) {
    int driverPid = service.getProcessId();
    killProcessTree(driverPid);
}

3. 添加Chrome启动参数优化进程清理

启动Chrome时添加以下参数,强制进程退出时彻底清理:

ChromeOptions options = new ChromeOptions();
options.addArguments(
    "--no-sandbox",          // 禁用沙箱,CentOS无界面环境必需
    "--disable-dev-shm-usage", // 避免/dev/shm空间不足
    "--headless=new",        // 无界面模式
    "--single-process",      // 单进程运行,减少进程数量(可选)
    "--disable-background-networking",
    "--disable-sync",
    "--no-first-run",
    "--safebrowsing-disable-auto-update"
);
// 将options传入desiredCapabilities
desiredCapabilities.setCapability(ChromeOptions.CAPABILITY, options);

4. 修正进程查询逻辑

原方法仅通过端口查询PID,遗漏大量子进程。改为通过ChromeDriver的PID查询所有关联进程,或者直接使用pkill -f chrome(需谨慎,避免误杀其他Chrome进程):

// 可选:批量杀死所有chrome相关进程(仅限当前任务专属场景)
Runtime.getRuntime().exec("pkill -f chrome");

5. 回收Java子进程资源

使用Runtime.exec()后,需调用process.waitFor()和关闭流,避免子进程变成僵尸:

private static List<Integer> GetChromePidFromPort(Integer port) throws IOException, InterruptedException {
    List<Integer> pidList = new ArrayList<>();
    Process process = Runtime.getRuntime().exec("lsof -i:" + port);
    BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
    String line;
    while ((line = reader.readLine()) != null) {
        if (line.contains("chrome")) {
            int pid = Integer.parseInt(line.split("\\s+")[1]);
            pidList.add(pid);
        }
    }
    reader.close();
    process.waitFor(); // 等待进程执行完毕,回收资源
    return pidList;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 14:37:58