【问题标题】:perl regex using too much memory?perl 正则表达式使用太多内存?
【发布时间】:2014-06-16 05:06:06
【问题描述】:

我有一个 perl 例程导致我在系统中经常出现“内存不足”问题。

脚本做了三件事

1> get the output of a  command to an array   (@arr = `$command`    --> array will hold about 13mb of data after the command)
2> Use a large regex to match the contents of individual array elements  -->

The regex is something like this
if($new_element =~ m|([A-Z0-9\-\._\$]+);\d+\s+([0-9]+)-([A-Z][A-Z][A-Z])-([0-9][0-9][0-9][0-9]([0-9]+)\:([0-9]+)\:([0-9]+)|io) 
<put to hash>
3> Put the array in a persistent hash map.
$hash_var{arr[0]} = "Some value"

编辑: 正则表达式处理的样本数据是

Z4:[newuser.newdir]TESTOPEN_ERROR.COM;4
                                                    8-APR-2014 11:14:12.58
Z4:[newuser.newdir]TEST_BOC.CFG;5
                                                    5-APR-2014 10:43:11.70
Z4:[newuser.newdir]TEST_BOC.COM;20
                                                    5-APR-2014 10:41:01.63
Z4:[newuser.newdir]TEST_NEWRT.COM;17
                                                    4-APR-2014 10:30:56.11

大约 10000 行这样的

我首先怀疑数组和哈希一起可能会消耗太多内存。 但是我开始认为这个正则表达式也可能与内存不足有关。

perl 正则表达式(带有 'io' 选项!)真的是导致内存不足的罪魁祸首吗?

【问题讨论】:

  • 您是否尝试过使用调试器来查看在内存不足错误之前您能走多远?您能否提供示例数据以及您尝试过的方法?
  • 我正在使用带有 32 位 perl 映像的 openvms 系统。我不知道除 Devel:size 之外的任何调试器。打开以获取建议。相关问题:stackoverflow.com/questions/23354220/…
  • 我怀疑它是正则表达式,而不是你的 3>。 10000 个唯一行作为哈希键,加上“某些值”可能很昂贵。如果你的线条不是唯一的,你就会有重叠。不知道你真正想要什么
  • 您使用该正则表达式的目的是什么?提取信息?验证输入数据?或者是其他东西?为什么你认为正则表达式是你的问题的原因?
  • 我对数组和散列的大小进行了一些测试。结果数组的最大大小为 13 MB,散列小于 7 MB(即使散列的范围在函数之外)。正则表达式的目的是仅将有效信息输入到散列中。 “Some value”实际上是处理成posix格式的输入数据中的日期。哈希的输入大多是唯一的,因为键是文件名

标签: regex perl memory-management out-of-memory


【解决方案1】:

您真正想解决什么问题? 用你的话...不是 Perl。

类似于:“脚本从 openvms 目录输出命令中挑选输出,目的是报告按目录排序的文件数和最早日期”

第一个问题是为什么要保留数组。剧本会再次“走”它吗? 如果没有,就在那里处理,然后在 for 循环中处理。

正则表达式似乎挑选出文件名和日期。以前就是这样的。 这并不难,并且可以通过信任 OpenVMS 目录格式来简化。 像这样的东西读起来更好恕我直言:

if($new_element =~ m|](.*);\d+\s+(\d+)-(\w+)-(\d+)\s+(\d+):(d+):(\d+) |)

: $hash_var{arr[0]} =

嗯,这向我表明数组中的一整行用作键值,所有 50 多个空格。因此,这 10,000 行调整为 1,000,000+ 字节,仅用于原始密钥字节。很多但不疯狂。新我们知道该行的第一个单词必须是唯一的,为什么不利用它: $hash_var{$1} = xxx if /(\S+)/l;

该程序可能还想利用前导字符串高度重复,并将“]”之前的所有内容替换为不断增加的目录号,并以“look-a-side”数组和/或散列形式维护。

就我个人而言,我会从命令中删除 /NOHE​​AD,并使用正则表达式来获取目录,因为它们在自己的行中出现。

或者使用 SUBSTR 或其他...当然,您需要在重新访问时构造一个类似的密钥。

在相关主题中,打印了调试输出。 也许在数组中包含行号以供您自己理解?

Perl encounters "out of memory" in openvms system

祝你好运! 海因

【讨论】:

    【解决方案2】:

    这与正则表达式无关。

    如果您在内存受限的环境中操作,您应该一次处理一条数据记录,而不是一次获取所有记录。假设您像这样提取数据:

    my @data = `some command`;
    for my $line (@data) {
        ... # process the line
    }
    

    这非常浪费,因为您需要存储数据和处理的输出(在您的情况下:哈希)。

    改为逐行处理输入。我们可以使用open 函数代替反引号:

    open my $cmd, '-|', 'some', 'command' or die "Can't run some command: $!";
    while (my $line = <$cmd>) {
        ... # process the line
    }
    

    这里不需要数组,这为我们节省了 13MB 的内存,我们现在可以将其用于其他用途。

    【讨论】:

    • 我已经这样做了,并进行了测试。等着看它是否再次出现内存不足这样我就可以确定它不是数组或哈希。
    • 我在 open 中使用了 $PIPE。如果在我的手术后是否需要关闭? eg: close $PIPE or die ("Not able to close") if ;
    • @KiranBangalore 是的,应该手动关闭管道:close $cmd or die "Couldn't close some command: $!"。另请参阅 $?${^CHILD_ERROR_NATIVE} 变量,它们将包含命令的退出状态。
    猜你喜欢
    • 1970-01-01
    • 2013-08-25
    • 1970-01-01
    • 2010-09-15
    • 1970-01-01
    • 1970-01-01
    • 2014-08-17
    • 2010-11-25
    • 1970-01-01
    相关资源
    最近更新 更多