是否需要手动销毁和关闭 java.lang.Process 的打开流?

Posted

技术标签:

【中文标题】是否需要手动销毁和关闭 java.lang.Process 的打开流?【英文标题】:Is it required to manually destroy and close the open streams of a java.lang.Process? 【发布时间】:2020-10-20 03:37:21 【问题描述】:

我有以下代码:

Process proc;
try

    ProcessBuilder procBuilder = new ProcessBuilder(/* some args */);
    proc = procBuilder.start();
    if (proc.waitFor(30000, TimeUnit.MILLISECONDS))
    
        //Etc...
    
    else
    
        //Handle it
    

catch (InterruptedException ie)

    currentThread().interrupt();

finally

    //What goes here?

我试图找到一些表明是否需要调用 proc.destroy() 的来源(我应该在调用 destroy 之前检查 isAlive() 吗?),并手动关闭其输入/输出/错误流,但无济于事。据我所知,即使是官方文档也没有说明这一点。

当我完成生成的进程后执行这些操作是否有必要,甚至只是一种良好的做法?

【问题讨论】:

您当然应该关闭进程的输出流。否则它可能会阻止在其stdin 上等待流结束。 如果进程产生输出,你需要对它做一些事情。如果您不关心输出,请使用procBuilder.inheritIO();(在启动进程之前)使其与您的 Java 程序的输出出现在同一位置。 【参考方案1】:

在 finally 块中调用 process.destroyForcibly() 是个好主意。 waitFor 不会杀死进程,如果它没有在超时之前完成,所以在生成的进程无限期挂起的情况下,如果你不杀死它,它可能会永远存在。

另一个用例是假设您的进程需要大约 25 秒才能正常终止。假设您的线程在启动后几乎立即被中断。最好终止进程而不是让它一直运行,因为无论如何都不会使用结果。

关于流,见Properly closing Java Process InputStream from getInputStream

【讨论】:

【参考方案2】:

虽然Michael's answer 正确解决了答案的“破坏”部分,并且我回应了按照建议使用finally 块的建议,但最初的问题还询问了关于关闭流的问题,并且存在细微差别。

在 Linux、macOS 和 BSD Unix 平台上,destroy() 实现会自动关闭流,因此对于这些平台,只需销毁进程并获得流关闭就足够了。

但是,在 Windows 和 Solaris (SunOS) 平台上,本机 destroy() 实现仅执行 TerminateProcesskill 并且不会关闭流。 JDK8 source code 中甚至还有一个 StreamsSurviveDestroy 测试用例(仅限 Solaris)。

因此,如果您在代码中支持 Windows 或 Solaris,则除了在 finally 块中添加 destroy()destroyForcibly() 调用外,还应尝试关闭输入、输出和错误流。这个流闭包可以在destroy调用之前或之后完成。

【讨论】:

以上是关于是否需要手动销毁和关闭 java.lang.Process 的打开流?的主要内容,如果未能解决你的问题,请参考以下文章

我应该在销毁活动时手动关闭我的应用程序创建的 HandlerThreads 吗?

在会话自动销毁之前调用自定义方法

我是不是必须销毁对话框上手动创建的组合框?

Spring注解-生命周期与属性赋值

#yyds干货盘点#会话管理

我是不是需要手动关闭 EF 核心 Dbcontext 连接