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

Java ProcessBuilder启动进程成功,flush时抛出Stream closed异常

环境信息
  • 操作系统:Linux(Java程序运行在Docker容器内)
  • Java版本:1.8
  • Node版本:10.22.1
问题复现

尝试使用Java ProcessBuilder类基于可执行js文件创建进程,代码如下:

String command = "/node/app/bin/app.js"
final ProcessBuilder pb = new ProcessBuilder(command);
Process process = pb.start();
OutputStream out = process.getOutputStream();
out.write("\n[:--:]".getBytes());
out.flush();// 该行抛出异常 java.io.IOException: Stream closed

执行pb.start()后提示进程创建成功,调试信息截图如下:
调试信息截图

执行out.flush()时抛出异常,错误栈信息:

Caused by: java.io.IOException: Stream closed

at java.lang.ProcessBuilder$NullOutputStream.write(ProcessBuilder.java:433) ~[?:1.8.0_292]

at java.io.OutputStream.write(OutputStream.java:116) ~[?:1.8.0_292]

at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82) ~[?:1.8.0_292]

at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140) ~[?:1.8.0_292]

使用的/node/app/bin/app.js文件内容:

#!/usr/bin/env node
var path = require('path');

var apJsFile = path.resolve(__dirname, '../app.js'); //app.js 本身可正常运行

require(appJsFile);
异常原因

错误栈中出现ProcessBuilder$NullOutputStream,说明Java尝试向子进程标准输入写入数据时,子进程已经提前退出,JVM会将已退出子进程对应的标准输入流替换为无操作的空输出流,所有写入、刷新操作都会直接抛出Stream closed异常。
子进程提前退出的常见诱因:

  1. 直接将js脚本路径作为ProcessBuilder的执行命令时,部分Docker精简内核/环境不会自动解析脚本头部的shebang行,加上如果脚本没有配置可执行权限,会导致进程启动后立刻异常终止。
  2. 未显式配置ProcessBuilder的工作目录,js脚本内的相对路径解析出错,node加载目标app.js文件失败直接退出,根本没有运行到读取标准输入的逻辑。
  3. 未消费子进程的标准输出、标准错误流,子进程输出缓冲区占满后被系统强制终止。
修复方案
  • 显式指定node解释器作为执行命令,不要依赖shebang自动解析,同时配置正确的工作目录,兼容性最高:
// 命令第一个参数为node可执行文件的容器内绝对路径,第二个参数为脚本路径
List<String> command = Arrays.asList("/usr/local/bin/node", "/node/app/bin/app.js");
final ProcessBuilder pb = new ProcessBuilder(command);
// 配置脚本运行的工作目录,避免相对路径解析错误
pb.directory(new File("/node/app"));
// 将错误流重定向到标准输出,方便统一排查日志
pb.redirectErrorStream(true);
Process process = pb.start();
  • 启动独立线程消费子进程的输出流,避免缓冲区满导致子进程阻塞退出:
new Thread(() -> {
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
        String line;
        while ((line = reader.readLine()) != null) {
            // 按需处理子进程输出,例如打印业务日志
            System.out.println("子进程输出:" + line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}).start();
  • 写入标准输入前先确认子进程存活,用完流及时关闭:
if (process.isAlive()) {
    try (OutputStream out = process.getOutputStream()) {
        out.write("\n[:--:]".getBytes());
        out.flush();
    }
}
  • 前置校验:先进入容器手动执行node /node/app/bin/app.js,确认脚本本身可以正常常驻、不会秒退,且逻辑中确实存在读取标准输入的处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:54:21