从Linux内核/ libc版本是什么Java Runtime.exec()安全的内存方面?

在工作中,我们的目标平台之一是运行Linux的内核资源受限的小型服务器(内核2.6.13,基于旧的Fedora Core的自定义分发)。 该应用程序是用Java编写的(Sun JDK 1.6_04)。 Linux OOM杀手被configuration为在内存使用超过160MB时终止进程。 即使在高负载的情况下,我们的应用程序也不会超过120MB,并且与其他一些活动的本地进程一起,我们仍然能够保持在OOM限制之内。

但是,事实certificate,Java Runtime.getRuntime()。exec()方法是从Java执行外部进程的标准方法, 在Linux上有一个特别不幸的实现 ,导致派生subprocess(临时)需要相同数量的内存作为父进程,因为地址空间被复制。 最终的结果是我们的应用程序在我们执行Runtime.getRuntime()。exec()时被OOM杀手所杀害。

我们目前通过一个独立的本地程序来执行所有外部命令,并通过套接字与该程序进行通信。 这不是最佳的。

在网上发布这个问题后,我得到了一些反馈,指出这不应该发生在Linux的“更新”版本上,因为它们使用copy-on-write实现了posix fork()方法,这大概意味着它只会复制它需要的页面在需要的时候修改而不是立即整个地址空间。

我的问题是:

  • 这是真的?
  • 这是内核,libc实现还是其他地方?
  • 从什么版本的内核/ libc /无论是copy-on-write for fork()可用?

Solutions Collecting From Web of "从Linux内核/ libc版本是什么Java Runtime.exec()安全的内存方面?"

这几乎是* nix(和linux)从黎明以来的工作方式(或者在凌晨)。

要在* nixes上创建一个新的进程,可以调用fork()。 fork()用它的所有内存映射,文件描述符等创建一个调用进程的副本。内存映射是在写入时拷贝完成的,因此(在最佳情况下)没有内存被实际复制,只有映射。 接下来的exec()调用将当前的内存映射替换为新的可执行文件映射。 所以,fork()/ exec()是你创建一个新进程的方式,这就是JVM所使用的。

警告在繁忙的系统上有巨大的进程,父进程可能会继续运行一段时间,之后exec()会导致大量的内存被复制,这是因为写入时复制造成的。 在虚拟机中,内存可以移动很多,以方便垃圾收集器,从而产生更多的复制。

“解决方法”是完成你已经完成的任务,创建一个外部轻量级进程来处理产生新进程 – 或者使用比fork / exec更轻量级的方法来产生进程(哪些linux没有 – 反正需要改变jvm本身)。 Posix指定了posix_spawn()函数,理论上它可以在不复制调用进程的内存映射的情况下实现 – 但在Linux上则不是。

好吧,我个人怀疑这是真的,因为Linux的fork()是通过copy-on-write完成的,因为上帝知道什么时候(至少有2.2.x的内核,它是在199x的某个地方)。

由于OOM杀手被认为是一种相当粗糙的工具,已知会失火(因为没有必要杀死实际分配大部分记忆的过程),而且只能用作最后的记忆,所以并不清楚我为什么你把它配置成在160M上开火。

如果你想对内存分配施加限制,那么ulimit是你的朋友,而不是OOM。

我的建议是将OOM单独留下(或完全禁用),配置ulimits,并忘记这个问题。

是的,即使是新版本的Linux也是如此(我们使用的是64位Red Hat 5.2)。 我一直有一个缓慢运行的子进程18个月左右的问题,直到我看到你的问题,并运行测试来验证它,永远不会找出问题。

我们有一个16 GB的32 GB的盒子,如果我们使用-Xms4g和-Xmx8g这样的设置来运行JVM,并且使用带有16个线程的Runtime.exec()来运行子进程,那么我们不能运行我们的进程比大约20每秒处理调用。

在Linux中使用简单的“日期”命令尝试这个约10,000次。 如果您添加分析代码来观察发生的情况,它会很快开始,但会随着时间的推移而减慢。

看完你的问题后,我决定尝试降低我的记忆设置-Xms128m和-Xmx128m。 现在我们的进程每秒运行约80个进程。 JVM的内存设置是我改变的。

它似乎并没有以这样的方式吸引我的内存,甚至当我用32个线程来尝试时,我的内存已经耗尽了。 这只是额外的内存必须以某种方式分配,这会导致沉重的启动(也可能是关机)成本。

无论如何,似乎应该有一个设置来禁用这种行为Linux甚至可能在JVM中。

1:是的。 2:这分为两个步骤:像fork()这样的任何系统调用都被glibc包装到内核中。 系统调用的内核部分在kernel / fork.c中3:我不知道。 但是我敢打赌你的内核有它。

低内存在32位机器上受到威胁时,OOM杀手将会启动。 我从来没有遇到过这个问题,但是有办法让OOM停下来。 这个问题可能是一些OOM配置问题。

由于您正在使用Java应用程序,因此您应该考虑迁移到64位Linux。 这绝对应该解决它。 只要安装了相关的库,大多数32位应用程序都可以在64位内核上运行,而不会出现任何问题。

你也可以尝试32位的Fedora的PAE内核。