3

在 Perl 中,我生成了一个巨大的只读数据结构,然后fork().

这是为了在分叉时利用 RSS 页面上的 COW。它工作得非常好,但是当一个子进程退出时,它会在死亡之前从 itelf 分配所有 RAM。

有没有办法避免这种无用的分配?

这是显示该问题的示例 Perl 代码。

#! /usr/bin/perl
my $a = [];
# Allocate 100 MiB
for my $i (1 .. 100000) {
        push @$a, "x" x 1024;
}
# Fork 10 other process
for my $j (1 .. 10) {
        last unless fork();
}
# Sleep for a while to be able to see the RSS
sleep(5);

在示例vmstat输出中,我们可以看到它首先只分配了 100MiB,然后在第一次睡眠后它分配了一小段时间,然后全部释放。

procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 1329660  80596  86936    0    0    21    18  160   25  0  0 100  0  0
 1  0      0 1328048  80596  86936    0    0     0     0 1013   44  0  0 100  0  0
 0  0      0 1223888  80596  86936    0    0     0     0 1028   76 11  5 84  0  0
 0  0      0 1223888  80596  86936    0    0     0     0 1010   40  0  0 100  0  0
 0  0      0 1223888  80596  86936    0    0     0     0 1026   54  0  0 100  0  0
 0  0      0 1223888  80596  86936    0    0     0     0 1006   39  0  0 100  0  0
13  0      0 741156  80596  86936    0    0     0     0 1012   66 13 58 28  0  0
 0  0      0 1329288  80596  86936    0    0     0     0 1032   60  0  0 100  0  0

注意:这似乎不是 Perl 版本特定的问题。当我测试 5.8.8、5.10.1 和 5.14.2 时,它们都表现出这种行为。

更新:

正如@choroba 在评论中所问的那样,我也尝试undef了数据结构,但似乎它在分配 RAM 时触发了内存接触。

您可以在第一个脚本的末尾添加以下代码段。

# Unallocate $a
undef $a;
# Sleep for a while to be able to see the RSS
sleep(5);
4

1 回答 1

2

实际上,正如我自己发现的那样,这种行为是一种特性,答案就在Perl 文档中:

exit()函数并不总是立即退出。同样,任何需要调用的对象析构函数都会在真正退出之前调用。如果这是一个问题,你可以调用POSIX::_exit($status)来避免END和析构函数处理。

事实上,在原始代码示例的末尾添加它确实可以避免这种行为。

# XXX - To be added just before ending the process
# Use POSIX::_exit($status) to end without allocating copy-on-write RAM
use POSIX;
POSIX::_exit(0);

注意:为此,孩子也必须在数据结构超出范围之前退出。

于 2013-04-16T11:20:39.443 回答