在Perl中避免在退出时分配只读的forked() RAM



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

这是为了在分叉时利用RSS页面上的COW。它工作得很好,但是当子进程退出时,它会在死亡之前从自己分配所有的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,然后在第一次睡眠后,它分配了一小段时间的全部mib,然后释放了全部mib。

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);

实际上,正如我自己发现的,这种行为是一个特性,答案在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);

注意:要使此操作生效,子节点也必须在数据结构超出作用域之前退出

最新更新