Linux 当子进程接触变量元数据时,如何防止内存爆炸?

Linux 当子进程接触变量元数据时,如何防止内存爆炸?,linux,performance,perl,memory-management,Linux,Performance,Perl,Memory Management,Linux使用fork来保持低内存使用率,但Perl 5变量在Perl中的工作方式似乎无法实现这种优化。例如,对于变量: my $s = "1"; perl实际上是在存储: SV = PV(0x100801068) at 0x1008272e8 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0x100201d50 "1"\0 CUR = 1 LEN = 16 在数字上下文中使用该字符串时,它会修改表示数据的Cstruct: SV = PVIV(0x

Linux使用fork来保持低内存使用率,但Perl 5变量在
Perl
中的工作方式似乎无法实现这种优化。例如,对于变量:

my $s = "1";
perl
实际上是在存储:

SV = PV(0x100801068) at 0x1008272e8
  REFCNT = 1
  FLAGS = (POK,pPOK)
  PV = 0x100201d50 "1"\0
  CUR = 1
  LEN = 16
在数字上下文中使用该字符串时,它会修改表示数据的C
struct

SV = PVIV(0x100821610) at 0x1008272e8
  REFCNT = 1
  FLAGS = (IOK,POK,pIOK,pPOK)
  IV = 1
  PV = 0x100201d50 "1"\0
  CUR = 1
  LEN = 16
字符串指针本身没有改变(它仍然是
0x100201d50
),但现在它位于不同的C
结构中(a
PVIV
,而不是a
PV
)。我根本没有修改价值,但突然我支付了奶牛成本。有没有办法锁定perl 5变量的
perl
表示,这样节省时间(
perl
不必再次将
“0”
转换为
0
)的黑客攻击不会影响我的内存使用

注意,上面的表示是根据此代码生成的:

perl -MDevel::Peek -e '$s = "1"; Dump $s; $s + 0; Dump $s'

到目前为止,我找到的唯一解决方案是确保我强制
perl
执行父进程中预期的所有转换。您可以从下面的代码中看到,即使这样也只会有一点帮助

结果:

Useless use of addition (+) in void context at z.pl line 34.
Useless use of addition (+) in void context at z.pl line 45.
Useless use of addition (+) in void context at z.pl line 51.
before eating memory
used memory: 71
after eating memory
used memory: 119
after 100 forks that don't reference variable
used memory: 144
after children are reaped
used memory: 93
after 100 forks that touch the variables metadata
used memory: 707
after children are reaped
used memory: 93
after parent has updated the metadata
used memory: 109
after 100 forks that touch the variables metadata
used memory: 443
after children are reaped
used memory: 109
代码:


无论如何,若你们在开始和跑步时避免奶牛,你们不应该忘记生命的结束阶段。在shutdown中有两个GC阶段,第一个阶段是ref计数更新,所以它可以很好地杀死您。您可以通过以下方式解决此问题:

END { kill 9, $$ }

这是不言而喻的,但是COW并不是在每个结构的基础上发生的,而是在内存页的基础上发生的。所以,对整个内存页中的一件事情进行这样的修改,就足以让您支付复制成本

在Linux上,您可以按如下方式查询页面大小:

getconf PAGESIZE
在我的系统上是4096字节。您可以在该空间中容纳许多Perl标量结构。如果其中一个东西被修改,Linux将不得不复制整个东西


这就是为什么使用内存竞技场通常是一个好主意。您应该将可变数据和不可变数据分开,这样您就不必为不可变数据支付COW成本,因为它恰好与可变数据位于同一个内存页中。

Nice,但是您知道当分配几百MB的数据时,会发生什么事,会产生几个子数据,而这些子数据会结束吗?GC会杀了你的。这是一个悲哀的故事,但是Perl对于这种工作来说是一个错误的工具。我们使用END{kill 9$$$}方法部分解决了这个问题,但此时您应该寻找更好的工具;-)GC并不困扰我,真正的代码是基于
mod_perl
的,每个子代码都被多次重用。问题是,加载到父级的配置数据被复制到可能有数百个子级的每个子级中,即使这些子级从未修改过配置数据(从Perl 5的角度来看,
Perl
正在处理元数据)。我考虑过的另一个解决方案是将配置数据移出一个单独的进程,让子进程通过域套接字与之对话。您也可以使用更快的共享内存。@Hynek如果全局销毁阶段困扰您,您最好调用
exec'/bin/true/
或使用
POSIX::\u exit()
。这两种方法都是跳过对象销毁的有文档记录的方法。@exec下的perlfunc中的Chas“注意,exec不会调用您的结束块,也不会调用对象中的任何销毁方法。”在POSIX中“它会立即退出程序,这意味着除其他外,缓冲I/O不会刷新。”在perlfunc中的exit“exit()函数并不总是立即退出。它首先调用任何已定义的结束例程,但这些结束例程本身可能不会中止退出。同样,需要调用的任何对象析构函数都会在实际退出之前调用。如果这是一个问题,您可以调用POSIX:_exit($status),以避免结束和析构函数处理”您的答案没有多大意义。@在程序的末尾,所有变量都将获得GC递减的
REFCNT
字段。当所有变量都被拉入子进程时,这将导致内存使用突然激增。他建议的代码将导致孩子在垃圾收集阶段开始之前死亡。问题是
perl
更新
struct
,即使我不更改任何我关心的数据,所以我不能分离我的不可变数据(因为没有不可变数据)。此外,没有一种简单的方法来区分可变数据和不可变数据(即使有)。
getconf PAGESIZE