Java Runtime.getRuntime().exec() 替代方案

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2876964/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 13:57:22  来源:igfitidea点击:

Java Runtime.getRuntime().exec() alternatives

javatomcatmemory-managementruntime.exec

提问by twilbrand

I have a collection of webapps that are running under tomcat. Tomcat is configured to have as much as 2 GB of memory using the -Xmx argument.

我有一组在 tomcat 下运行的 web 应用程序。使用 -Xmx 参数将 Tomcat 配置为具有多达 2 GB 的内存。

Many of the webapps need to perform a task that ends up making use of the following code:

许多 web 应用程序需要执行最终使用以下代码的任务:

Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(command);
process.waitFor();
...

The issue we are having is related to the way that this "child-process" is getting created on Linux (Redhat 4.4 and Centos 5.4).

我们遇到的问题与在 Linux(Redhat 4.4 和 Centos 5.4)上创建这个“子进程”的方式有关。

It's my understanding that an amount of memory equal to the amount tomcat is using needs to be free in the pool of physical (non-swap) system memory initially for this child process to be created. When we don't have enough free physical memory, we are getting this:

我的理解是,为了创建这个子进程,最初需要在物理(非交换)系统内存池中释放与 tomcat 使用量相等的内存量。当我们没有足够的可用物理内存时,我们会得到:

    java.io.IOException: error=12, Cannot allocate memory
     at java.lang.UNIXProcess.<init>(UNIXProcess.java:148)
     at java.lang.ProcessImpl.start(ProcessImpl.java:65)
     at java.lang.ProcessBuilder.start(ProcessBuilder.java:452)
     ... 28 more

My questions are:

我的问题是:

1) Is it possible to remove the requirement for an amount of memory equal to the parent process being free in the physical memory?I'm looking for an answer that allows me to specify how much memory the child process gets or to allow java on linux to access swap memory.

1) 是否可以取消对与父进程在物理内存中空闲的内存量相等的要求?我正在寻找一个答案,它允许我指定子进程获得多少内存或允许 linux 上的 java 访问交换内存。

2) What are the alternatives to Runtime.getRuntime().exec() if no solution to #1 exists?I could only think of two, neither of which is very desirable. JNI (very un-desirable) or rewriting the program we are calling in java and making it it's own process that the webapp communicates with somehow. There has to be others.

2) 如果 #1 的解决方案不存在,Runtime.getRuntime().exec() 的替代方法是什么?我只能想到两个,都不是很理想。JNI(非常不受欢迎)或重写我们在java中调用的程序并使其成为webapp以某种方式与之通信的自己的进程。必须有其他人。

3) Is there another side to this problem that I'm not seeing that could potentially fix it?Lowering the amount of memory used by tomcat is not an option. Increasing the memory on the server is always an option, but seems like more a band-aid.

3) 这个问题还有其他方面我没有看到可以解决它吗?降低 tomcat 使用的内存量不是一种选择。增加服务器上的内存始终是一种选择,但似乎更像是一种创可贴。

Servers are running java 6.

服务器正在运行 java 6。

EDIT: I should specify that I'm not looking for a tomcat specific fix. This problem can be seen with any of the java applications we have running on the webserver (there are multiple). I simply used tomcat as an example because it will most likely have the most memory allocated to it and it's where we actually saw the error the first time. It is a reproducible error.

编辑:我应该指定我不是在寻找特定于 tomcat 的修复程序。我们在网络服务器上运行的任何 Java 应用程序(有多个)都可以看到这个问题。我只是以 tomcat 为例,因为它很可能分配了最多的内存,而且这是我们第一次真正看到错误的地方。这是一个可重现的错误。

EDIT: In the end, we solved this problem by re-writing what the system call was doing in java. I feel that we were pretty lucky being able to do this without making additional system calls. Not all processes will be able to do this, so I would still love to see an actual solution to this.

编辑:最后,我们通过重写系统调用在 java 中所做的事情来解决这个问题。我觉得我们很幸运能够在不进行额外系统调用的情况下做到这一点。并非所有流程都能做到这一点,所以我仍然希望看到一个实际的解决方案。

回答by Ian McLaird

Try using a ProcessBuilder. The Docs say that's the "preferred" way to start up a sub-process these days. You should also consider using the environment map (docs are in the link) to specify the memory allowances for the new process. I suspect (but don't know for certain) that the reason it needs so much memory is that it is inheriting the settings from the tomcat process. Using the environment map should allow you to override that behavior. However, note that starting up a process is very OS specific, so YMMV.

尝试使用ProcessBuilder。文档说这是现在启动子流程的“首选”方式。您还应该考虑使用环境映射(文档在链接中)来指定新进程的内存限额。我怀疑(但不确定)它需要这么多内存的原因是它继承了 tomcat 进程的设置。使用环境贴图应该允许您覆盖该行为。但是,请注意,启动进程是非常特定于操作系统的,所以 YMMV。

回答by Justin

I thinkthis is a unix fork() issue, the memory requirement comes from they way fork() works -- it first clones the child process image (at whatever size it currently is) then replaces the parent image with the child image. I know on Solaris there is some way to control this behavior, but I don't know offhand what it is.

认为这是一个 unix fork() 问题,内存需求来自 fork() 的工作方式——它首先克隆子进程映像(无论当前大小),然后用子映像替换父映像。我知道在 Solaris 上有一些方法可以控制这种行为,但我不知道它是什么。

Update: This is already explained in From what Linux kernel/libc version is Java Runtime.exec() safe with regards to memory?

更新:这已经在从什么 Linux 内核/libc 版本是 Java Runtime.exec() 安全的内存?

回答by luke

I found a workaround in this article, basically the idea is that you create a process early on in the startup of your application that you communicate with (via input streams) and then that subprocess executes your commands for you.

我在这篇文章中找到了一个解决方法,基本上这个想法是你在你的应用程序启动的早期创建一个与你通信(通过输入流)的进程,然后该子进程为你执行你的命令。

//you would probably want to make this a singleton
public class ProcessHelper
{
    private OutputStreamWriter output;
    public ProcessHelper()
    {
        Runtime runtime = Runtime.getRuntime();
        Process process = runtime.exec("java ProcessHelper");
        output = new OutputStreamWriter(process.getOutputStream());
    }
    public void exec(String command)
    {
        output.write(command, 0, command.length());
    }
}

then you would make a helper java program

然后你会做一个帮助java程序

public class ProcessHelper
{
    public static void main(String[] args)
    {
         BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
         String command;
         while((command = in.readLine()) != null)
         {
             Runtime runtime = Runtime.getRuntime();
             Process process = runtime.exec(command);
         }
    }
}

what we've essentially done is make a little 'exec' server for your application. If you initialize your ProcessHelper class early on in your application it will successfully create this process then you simply pipe commands over to it, because the second process is much smaller it should always succeed.

我们基本上所做的是为您的应用程序制作一个小小的“exec”服务器。如果您在应用程序的早期初始化 ProcessHelper 类,它将成功创建此进程,然后您只需将命令通过管道传递给它,因为第二个进程要小得多,它应该总是成功。

You could also make your protocol a little more in depth, such as returning exitcodes, notifying of errors and so on.

您还可以使您的协议更深入一些,例如返回退出代码、通知错误等。

回答by Kam

This would help I think. I know this is an old thread, just for future refs... http://wrapper.tanukisoftware.com/doc/english/child-exec.html

这将有助于我思考。我知道这是一个旧线程,仅供将来参考... http://wrapper.tanukisoftware.com/doc/english/child-exec.html

回答by gregturn

Another alternative is to have a separate exec process running that watches either a file or some other type of "queue". You append your desired command to that file, and the exec server spots it, running the command, and somehow writing the results to another location you can get.

另一种选择是运行一个单独的 exec 进程来监视文件或其他类型的“队列”。您将所需的命令附加到该文件,然后 exec 服务器发现它,运行该命令,并以某种方式将结果写入您可以获得的另一个位置。

回答by JoG

Best solution i found so far was using secure shell with public keys. Using http://www.jcraft.com/jsch/i created a connection to localhost and executed the commands like that. Maybe it has a little more overhead but for my situation it worked like a charm.

迄今为止我发现的最佳解决方案是使用带有公钥的安全外壳。使用http://www.jcraft.com/jsch/我创建了一个到 localhost 的连接并执行了这样的命令。也许它有更多的开销,但对于我的情况,它就像一个魅力。