我有一个单一的大堆(最大 240GB,虽然在这个执行阶段的大部分时间在 20-40GB 范围内)JVM [1] 在具有 24 个内核的服务器上运行在 Linux [2] 下。我们有成千上万的对象必须由外部可执行文件处理,然后将这些可执行文件创建的数据加载回 JVM。每个可执行文件都会产生大约 0.5 兆字节的数据(在磁盘上),当进程完成后直接读入这些数据时,当然会更大。
我们的第一个实现是让每个可执行文件只处理一个对象。这涉及生成两倍于我们拥有的对象的可执行文件(因为我们调用了调用可执行文件的 shell 脚本)。我们的 CPU 利用率一开始会很高,但不一定是 100%,然后会慢慢恶化。当我们开始测量以查看发生了什么时,我们注意到进程创建时间 [3] 不断变慢。虽然从亚秒级开始,但最终会增加到一分钟或更长时间。可执行文件完成的实际处理通常需要不到 10 秒。
接下来我们更改了可执行文件以获取要处理的对象列表,以尝试减少创建的进程数。批量大小为数百(约为我们当前样本大小的 1%)时,流程创建时间从大约 2 秒开始,然后增长到大约 5-6 秒。
基本上,为什么在执行过程中创建这些进程需要这么长时间?
[1]甲骨文JDK 1.6.0_22
[2] Red Hat Enterprise Linux Advanced Platform 5.3, Linux kernel 2.6.18-194.26.1.el5 #1 SMP
[3] 创建 ProcessBuilder 对象,重定向错误流,并启动它。
最佳答案
我的猜测是,如果 Java 使用 fork/exec 系统调用来生成子进程,您可能会遇到 fork/exec 问题。
通常 fork/exec 相当高效,因为 fork() 做的很少——所有页面都是写时复制。对于非常大的进程(即那些映射了千兆字节的页面的进程),这不再如此真实,因为页表本身需要相对较长的时间来创建 - 当然,销毁,因为您立即调用 exec。
由于您正在使用大量堆,这可能会影响您。您映射的页面越多,它可能会变得越糟糕,这可能是导致逐渐变慢的原因。
请考虑:
- 使用 posix_spawn,如果它不是由 libc 中的 fork/exec 实现的话
- 使用负责创建/收获其他子进程的单个子进程;产生一次并使用一些 IPC(管道等)告诉它该做什么。
注意:这都是猜测;您可能应该做一些实验,看看情况是否如此。
关于java - 在 Java 下创建进程变慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4571928/