我有一组在tomcat下运行的webapps。使用-Xmx参数将Tomcat配置为具有多达2 GB的内存。
许多Web应用程序需要执行最终使用以下代码的任务:
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(command);
process.waitFor();
...
我们遇到的问题与在Linux上创建“子进程”的方式有关(Redhat 4.4和Centos 5.4)。
我的理解是,等于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
我的问题是:
1)是否可以删除内存量等于父进程在物理内存中空闲的要求?我正在寻找一个允许我指定多少内容的答案内存子进程获取或允许java上的java访问交换内存。
2)如果没有#1的解决方案存在,Runtime.getRuntime()。exec()的替代方法是什么?我只能想到两个,这两个都不是很理想。 JNI(非常不可取)或重写我们在java中调用的程序,并使其成为webapp以某种方式与之通信的自己的进程。必须有其他人。
3)这个问题的另一面是否我没有看到它可能会修复它?降低tomcat使用的内存量是不可取的。增加服务器上的内存总是一种选择,但似乎更像是一种创可贴。
服务器正在运行java 6.
编辑:我应该指定我不是在寻找特定于tomcat的修复程序。我们在Web服务器上运行的任何java应用程序都可以看到这个问题(有多个)。我只是使用tomcat作为一个例子,因为它很可能会分配最多的内存,而这正是我们第一次看到错误的地方。这是一个可重现的错误。
编辑:最后,我们通过重写系统调用在java中执行的操作来解决此问题。我觉得我们很幸运能够在不进行额外系统调用的情况下完成此操作。并非所有流程都能够做到这一点,所以我仍然希望看到一个实际的解决方案。
答案 0 :(得分:6)
我在这个article找到了一个解决方法,基本上我的想法是你在应用程序启动时尽早创建一个进程(通过输入流),然后该子进程为你执行命令。
//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());
}
}
然后你会做一个帮助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);
}
}
}
我们基本上做的是为您的应用程序创建一个'exec'服务器。如果您在应用程序的早期初始化ProcessHelper类,它将成功创建此过程,然后您只需将命令传递给它,因为第二个过程要小得多,它应该总是成功。
您还可以更深入地制定协议,例如返回exitcodes,通知错误等等。
答案 1 :(得分:5)
尝试使用ProcessBuilder。 Docs说这是现在启动子流程的“首选”方式。您还应该考虑使用环境贴图(文档在链接中)来指定新进程的内存容量。我怀疑(但不确定)它需要如此多内存的原因是它继承了tomcat进程的设置。使用环境贴图应该允许您覆盖该行为。但请注意,启动流程非常符合操作系统,因此YMMV。
答案 2 :(得分:2)
我认为这是一个unix fork()问题,内存需求来自fork()的工作方式 - 它首先克隆子进程映像(无论当前大小如何)然后用子图像替换父图像。我知道在Solaris上有一些控制这种行为的方法,但我不知道它是什么。
更新:这已在From what Linux kernel/libc version is Java Runtime.exec() safe with regards to memory?
中说明答案 3 :(得分:1)
这有助于我思考。我知道这是一个旧线程,仅供将来参考... http://wrapper.tanukisoftware.com/doc/english/child-exec.html
答案 4 :(得分:1)
另一种方法是运行一个单独的exec进程来监视文件或其他类型的“队列”。您将所需的命令附加到该文件,并且exec服务器发现它,运行命令,并以某种方式将结果写入您可以获得的其他位置。
答案 5 :(得分:1)
到目前为止我发现的最佳解决方案是使用带有公钥的安全shell。使用http://www.jcraft.com/jsch/我创建了与localhost的连接并执行了这样的命令。也许它有更多的开销,但对于我的情况,它就像一个魅力。