在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);
注意:要使此操作生效,子节点也必须在数据结构超出作用域之前退出