4

我有一台配备 32GB RAM 的 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 篇文章),这是我的 ulimit。虚拟内存默认为无限制

$ ulimit -a | grep 字节
数据段大小 (kbytes, -d) 无限制
最大锁定内存 (kbytes, -l) 无限制
最大内存大小 (kbytes, -m) 无限制
管道大小(512 字节,-p)1
堆栈大小(千字节,-s)8192
虚拟内存 (kbytes, -v) 无限制

$ perl -E '我的 $x = "a" x 1200000000; 打印“确定\n”'
perl(23074) malloc: *** mmap(size=1200001024) 失败(错误代码=12)
*** 错误:无法分配区域
*** 在 malloc_error_break 中设置断点进行调试
内存不足!

$ perl -E '我的 $x = "a" x 1100000000; 打印“确定\n”'
行

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

$ ulimit -v 10000000000 # 100亿

$ perl -E '我的 $x = "a" x 1200000000; 打印“确定\n”'
perl(24275) malloc: *** mmap(size=1200001024) 失败(错误代码=12)
*** 错误:无法分配区域
*** 在 malloc_error_break 中设置断点进行调试
内存不足!
4

4 回答 4

6

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

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

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

(出于某种奇怪的原因,perl -V:use64bitall说这已经完成了,但显然没有。)

于 2013-11-14T16:44:35.157 回答
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

哎呀。如果你有一个,那肯定会达到 32 位的限制。

我被宠坏了,在 上进行测试5.19.5,它有一个显着的改进,即写时复制字符串,它大大减少了内存消耗:

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 字符串来达到该限制。

为什么写时复制很重要?

好吧,当你这样做的时候

$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
于 2013-11-15T00:31:09.203 回答
1

也可能是 Mac 对每个进程的内存施加了限制,以防止进程消耗过多的系统内存。

我不知道这有多有效,但我假设作为 Unix 的 Mac 具有类似 unix 的 ulimit:

有一些这样的内存限制,一些摘自/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也有一些值得研究的地方。

于 2013-11-14T17:40:32.777 回答
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 -V32 位和 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

谢谢大家的帮助,

保罗

于 2013-11-15T13:15:12.343 回答