Perl无法在具有32 GB RAM的Snow leopard Mac服务器上分配超过1.1 GB的空间

时间:2013-11-14 16:24:38

标签: perl out-of-memory

我有一台32GB内存的Mac服务器(雪豹)。当我尝试在Perl(v 5.10.0)中分配超过1.1GB的RAM时,出现内存不足错误。这是我使用的脚本:

#!/usr/bin/env perl

# My snow leopard MAC server runs out of memory at >1.1 billion bytes.  How
# can this be when I have 32 GB of memory installed?  Allocating up to 4
# billion bytes works on a Dell Win7 12GB RAM machine.

# perl -v
# This is perl, v5.10.0 built for darwin-thread-multi-2level
# (with 2 registered patches, see perl -V for more detail)

use strict;
use warnings;

my $s;
print "Trying 1.1 GB...";
$s = "a" x 1100000000;   # ok
print "OK\n\n";

print "Trying 1.2 GB...";
$s = '';
$s = "a" x 1200000000;   # fails
print "..OK\n";

以下是我得到的输出:

Trying 1.1 GB...OK

perl(96685) malloc: *** mmap(size=1200001024) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
Out of memory!
Trying 1.2 GB...

为什么会发生这种情况?


2013年11月14日下午4:42更新

根据Kent Fredric(见下面的2个帖子),这是我的ulimits。虚拟内存默认为无限

$ ulimit -a | grep bytes
data seg size           (kbytes, -d) unlimited
max locked memory       (kbytes, -l) unlimited
max memory size         (kbytes, -m) unlimited
pipe size            (512 bytes, -p) 1
stack size              (kbytes, -s) 8192
virtual memory          (kbytes, -v) unlimited

$ perl -E 'my $x = "a" x 1200000000; print "ok\n"'
perl(23074) malloc: *** mmap(size=1200001024) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
Out of memory!

$ perl -E 'my $x = "a" x 1100000000; print "ok\n"'
ok

我尝试将虚拟内存设置为100亿但无济于事。

$ ulimit -v 10000000000   # 10 billion

$ perl -E 'my $x = "a" x 1200000000; print "ok\n"'
perl(24275) malloc: *** mmap(size=1200001024) failed (error code=12)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
Out of memory!

4 个答案:

答案 0 :(得分:6)

您使用的是32位版本的Perl(如perl -V:ptrsize所示),但您需要64位版本。我建议使用perlbrew安装本地perl

这可以通过在安装Perl时将-Duse64bitall传递给Configure来实现。

这可以通过在安装Perl时将--64all传递给perlbrew install来实现。

(由于一些奇怪的原因,perl -V:use64bitall说这已经完成,但显然不是。)

答案 1 :(得分:3)

似乎这可能与问题有关。这只是值得评论,但它过于复杂而不能完全无法识别

perlbrew exec --with=5.10.0 memusage perl -e '$x = q[a] x 1_000_000_000; print length($x)'
5.10.0
==========
1000000000
Memory usage summary: heap total: 2000150514, heap peak: 2000141265, stack peak: 4896

是的,对于 1 G的文字, 2 G

现在有了2G ......

perlbrew exec --with=5.10.0 memusage perl -e '$x = q[a] x 1_000_000_000; $y = q[a] x 1_000_000_000; print length($x)+length($y)'
5.10.0
==========
2000000000
Memory usage summary: heap total: 4000151605, heap peak: 4000142092, stack peak: 4896

让人惊讶。如果你有一个肯定会达到32Bit的限制。

我被宠坏了并且在5.19.5上进行了测试,这有一个显着的改进,名为copy-on-write字符串,大大减少了内存消耗:

perlbrew exec --with=5.19.5 memusage perl -e '$x = q[a] x 1_000_000_000; $y = q[a] x 1_000_000_000; print length($x)+length($y)'
5.19.5
==========
2000000000
Memory usage summary: heap total: 2000157713, heap peak: 2000150396, stack peak: 5392

无论哪种方式,如果您使用的是除开发之外的任何版本的Perl,您需要期望它吃掉所需内存的两倍。

如果出于某种原因在32位进程的2G窗口周围存在内存限制,那么 将使用1G字符串命中。

为什么Copy On Write很重要?

嗯,当你做的时候

$a = $b

$a$b

副本

所以当你这样做时

$a = "a" x 1_000_000_000

首先,它展开右侧,创建变量,然后将副本存储在$a中。

您可以通过删除副本来证明这一点:

perlbrew exec --with=5.10.0 memusage perl -e 'print length(q[a] x 1_000_000_000)'
5.10.0
==========
1000000000
Memory usage summary: heap total: 1000150047, heap peak: 1000140886, stack peak: 4896

看,我所做的就是删除了中间变量,内存使用量减半!

:S

虽然因为5.19.5仅引用原始字符串,并在写入时将其复制,但默认情况下它是有效的,因此删除中间变量的好处可以忽略不计

perlbrew exec --with=5.19.5 memusage perl -e 'print length(q[a] x 1_000_000_000)'
5.19.5
==========
1000000000
Memory usage summary: heap total: 1000154123, heap peak: 1000145146, stack peak: 5392

答案 2 :(得分:1)

它也可能是Mac对每进程内存施加的限制,以防止进程占用过多的系统内存。

我不知道这会有多有效,但我认为作为Unix的Mac有类似unix的ulimits:

有一些此类内存限制,部分摘录自/etc/security/limits.conf

- core - limits the core file size (KB)
- data - max data size (KB)
- fsize - maximum filesize (KB)
- memlock - max locked-in-memory address space (KB)
- rss - max resident set size (KB)
- stack - max stack size (KB)
- as - address space limit (KB)

bash提供了限制和阅读这些内容的方法(有些)info bash --index-search=ulimit

例如,ulimit -a | grep bytes在我的Linux机器上发出此信息:

data seg size           (kbytes, -d) unlimited
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
stack size              (kbytes, -s) 8192
virtual memory          (kbytes, -v) unlimited

我可以在范围内随意限制:

$ perl -E 'my $x = "a" x 100000000;print "ok\n"'
ok
$ ulimit -v 200000
$ perl -E 'my $x = "a" x 100000000;print "ok\n"'
Out of memory!
panic: fold_constants JMPENV_PUSH returned 2 at -e line 1.

所以ulimits肯定是值得研究的。

答案 3 :(得分:1)

我想我明白了。我不能接受Apple在他们的文档说不同的情况下发送了32位Perl。来自'man perl':

64-BIT SUPPORT
Version 5.10.0 supports 64-bit execution (which is on by default).  Version 5.8.8
only supports 32-bit execution.

然后我记得,我在我的Mac服务器上安装了Fink,而且,它是32个和64位问题。所以,我评论了

#test -r /sw/bin/init.sh && . /sw/bin/init.sh

来自我的.profile。现在我至少可以在32 GB RAM服务器上分配14 GB RAM(是的!)

$ perl -E 'my $x = "a" x 14000000000; print "ok\n"'
ok

我尝试了16GB,但在我放弃之前它挂了5分钟。现在,diff之间的perl -V为32位和64位告诉故事(但为什么仍然intsize=4?)。

$ diff perlv.32 perlv.64
16c16
<     intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
---
>     intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
18c18
<     ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
---
>     ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
34,35c34,36
<                         PERL_IMPLICIT_CONTEXT PERL_MALLOC_WRAP USE_ITHREADS
<                         USE_LARGE_FILES USE_PERLIO USE_REENTRANT_API
---
>                         PERL_IMPLICIT_CONTEXT PERL_MALLOC_WRAP USE_64_BIT_ALL
>                         USE_64_BIT_INT USE_ITHREADS USE_LARGE_FILES
>                         USE_PERLIO USE_REENTRANT_API

谢谢大家的帮助,