gpt4 book ai didi

java - 从哪个 Linux 内核/libc 版本开始,Java Runtime.exec() 就内存而言是安全的?

转载 作者:IT老高 更新时间:2023-10-28 20:35:23 24 4
gpt4 key购买 nike

在工作中,我们的目标平台之一是运行 Linux(内核 2.6.13,基于旧 Fedora Core 的自定义发行版)的资源受限的微型服务器。该应用程序是用 Java (Sun JDK 1.6_04) 编写的。 Linux OOM killer 配置为在内存使用量超过 160MB 时终止进程。即使在高负载期间,我们的应用程序也永远不会超过 120MB,并且与其他一些 Activity 的 native 进程一起,我们保持在 OOM 限制内。

然而,事实证明 Java Runtime.getRuntime().exec() 方法,从 Java 执行外部进程的规范方法,具有 particularly unfortunate implementation on Linux这导致生成的子进程(暂时)需要与父进程相同的内存量,因为地址空间被复制。最终结果是,一旦我们执行 Runtime.getRuntime().exec(),我们的应用程序就会被 OOM killer 杀死。

我们目前通过让一个单独的 native 程序执行所有外部命令来解决此问题,并通过套接字与该程序进行通信。这不是最佳的。

posting about this problem online 之后我收到一些反馈,表明这不应该发生在“较新”版本的 Linux 上,因为它们使用写时复制实现了 posix fork() 方法,大概意味着它只会在需要时复制需要修改的页面。立即占用整个地址空间。

我的问题是:

  • 这是真的吗?
  • 这是在内核、libc 实现中还是完全在其他地方?
  • 从哪个版本的内核/libc/whatever 可以使用 fork() 的写时复制?

最佳答案

这几乎就是 *nix(和 linux)从时间之初(或至少在 mmus 之初)开始工作的方式。

要在 *nixes 上创建一个新进程,请调用 fork()。 fork() 创建调用进程及其所有内存映射、文件描述符等的副本。内存映射是在写入时复制的,因此(在最佳情况下)实际上没有复制内存,只有映射。随后的 exec() 调用将当前内存映射替换为新可执行文件的映射。因此,fork()/exec() 是您创建新进程的方式,这也是 JVM 使用的方式。

需要注意的是,在繁忙的系统上有大量进程,在子 exec() 导致大量内存被复制之前,父进程可能会继续运行一段时间,因为写入时复制。在 VM 中,内存可以移动很多,以促进垃圾收集器产生更多的复制。

“解决方法”是做你已经做过的事情,创建一个外部轻量级进程来处理生成新进程 - 或者使用比 fork/exec 更轻量级的方法来生成进程(哪个 linux 没有 -并且无论如何都需要更改jvm本身)。 Posix 指定了 posix_spawn() 函数,理论上可以在不复制调用进程的内存映射的情况下实现——但在 linux 上则不然。

关于java - 从哪个 Linux 内核/libc 版本开始,Java Runtime.exec() 就内存而言是安全的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/209875/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com