【问题标题】:Perl cannot allocate more than 1.1 GB on a Snow leopard Mac server with 32 GB RAMPerl 不能在具有 32 GB RAM 的 Snow leopard Mac 服务器上分配超过 1.1 GB 的空间
【发布时间】:2013-11-27 18:33:54
【问题描述】:

我有一台配备 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 中设置断点进行调试 记不清!

【问题讨论】:

  • 相关:Perl 是 64 位可执行文件吗?
  • 它是 32 位还是 64 位 perl?还有活动监视器说进程有多大?
  • 看起来像 64 位 pastebin.com/Bd3VLdpM
  • @Virushunter 不,它是 32 位 (ptrsize=4, ivsize=4)。

标签: perl out-of-memory


【解决方案1】:

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

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

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

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

【讨论】:

  • 你怎么知道我的 perl -V 输出 (pastebin.com/Bd3VLdpM) 有 32 位 perl?我认为 -arch x86_64 是 64 位的。谢谢
  • 嗯,我看到了use64bitall=define,但你也应该在编译时选项下有USE_64_BIT_ALL。你的构建有些奇怪。我的建议成立。
  • 这是 Mac 出厂版本。我想我必须尝试 perlbrew 看看它是否有效。我希望其他具有类似 Mac 服务器 Perl 配置的人能加入进来。再次感谢
  • @Virushunter 配置确实很奇怪。这可能是某种形式的跨平台构建:-arch x86_64 -arch i386 -arch ppc。无论如何,intsize=4, longsize=4, ptrsize=4 肯定是为 32 位系统编译的。这些是 C 类型的大小,而不是 Perl 数据结构的大小,因此您不能处理超过 4GB 的大小。 (这仍然不能完全解释 1.1GB 的截止)
  • 确实,1.2G 与 4G 的界限相差甚远。所以 64 位似乎不是直接原因。
【解决方案2】:

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

我不知道这有多有效,但我假设 Mac 作为 Unix 具有类似 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 肯定值得研究。

【讨论】:

  • (另外,对我来说它在 fold_constants 中失败的原因是因为 "a" x CONSTANT_NUMBER 将获得编译时翻译,因此在任何地方的代码中都会使之前的代码也失败;))
【解决方案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

【讨论】:

    【解决方案4】:

    我想我明白了。当他们的文档另有说明时,我无法接受 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 分钟。现在,32 位和 64 位之间的 perl -V 之间的 diff 说明了这个故事(但为什么仍然是 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
    

    谢谢大家的帮助,

    保罗

    【讨论】:

    • 因为整数总是 32 位 ;)。但是包含 perl 整数表示的 IV 是 8 而不是 4,这在这里很重要。 ptrsize 也是 8,这将内存寻址容量从 4G 限制增加到 16 PB 限制。只是如果你有假设 ptr 是整数的代码,那代码就是错误的 =)
    • 虽然我很高兴你找到了一个很好的答案来解决你的具体问题,但我仍然不明白为什么根本问题会在 2G 左右中断,这本身似乎到目前为止还无法解释。它的操作系统强制执行了很多挥手操作,但没有任何记录在任何地方。
    猜你喜欢
    • 2012-04-04
    • 2015-03-28
    • 2016-09-21
    • 2015-11-09
    • 2017-01-27
    • 1970-01-01
    • 1970-01-01
    • 2012-07-21
    • 2020-03-16
    相关资源
    最近更新 更多