识别Perl / DBI代码中的内存问题

时间:2011-03-29 16:54:46

标签: perl memory memory-leaks dbi

首先 - 这不是我的代码 - 代码存在问题,我试图找出如何调试问题。如果我有机会的话,我会对代码进行大量的更改(过多的大括号,全局变量,使用连接函数而不是foreach等等)。它充满了不好的做法,但这不是我需要帮助的。

这是Perl代码的片段(没有子程序,没什么特别的 - 基本上打开查询结果的文件,执行查询,并将结果转储到文件中):

# earlier in the program, @row, $field, and $output are all declared globally, like this:
my @row;
my $field;
my $output;

# a file is opened for output, with filehandle ROWOUT
# a database statement handle (DBD::DB2) is executed

while ( @{row} = ${sth}->fetchrow_array ) {
    foreach ${field}( @{row} ) {
        ${field} =~ s/\s+$//;
        ${output} = "${output}\~${field}";
    }

    ${output} =~ s/\~//;
    print ROWOUT "${output}\n";
    undef ${output};
}

在while循环中的某个地方,Perl脚本崩溃并出现Out of Memory!错误(不是干净的崩溃 - 它只是停止使用该消息运行。)

在大多数运行中,此查询的卷非常小。这次脚本崩溃时查询的结果要大得多(仍然不是很大):150,000行,每行大约1200字节宽。

我想到的事情:

  1. DBI的fetchrow_array函数足够聪明,不能将完整的数据集拉入内存,对吗?我的假设是数据在数据库上,并且fetchrow_array一次检索一行,所以即使你有100亿行,你也不应该有内存问题 - 这是正确的吗?
  2. undef变量上调用$output将释放它正在使用的内存,对吗?如果它没有,那可能是存在内存问题的另一个地方。
  3. 每次检索到新行时,@row变量正在使用的内存将被重用(?),对吗?如果没有,我可以看到使用全局数组存储每一行​​可能会耗尽内存。
  4. 我希望有一些明显的东西,我只是不理解。如果通过查看代码没有明显的东西,我可以使用哪些技术来调试这个问题?

    提前致谢!

3 个答案:

答案 0 :(得分:3)

可能是您(可能是无意中)缓存了太多行。您可以通过查看$sth->{RowsInCache}了解已引入的数量。如果它是undef,则没有缓存,否则您将获得行数。

您也可以通过以下方式重写您与$output所做的体操。

while ( my @this_row = $sth->fetchrow_array ) {
    # Get rid of this line once you figure out your memory problem.
    print STDERR "Using ", ($sth->{RowsInCache} || 0), " rows in cache\n";

    print ROWOUT join('~', map { s/\s+$// } @this_row), "\n";
}

因此,假设您的缓存中有太多行,您可以通过以下方式限制它:

my $dbh = DBI->connect($dsn, $user, $pass, { RowCacheSize => 20 })
    or die "Cannot connect to $dsn: $DBI::errstr\n";

从DBI文档中,您可以使用以下值控制缓存(假设您的驱动程序支持它):

 0 - Automatically determine a reasonable cache size for each C<SELECT>
 1 - Disable the local row cache
>1 - Cache this many rows
<0 - Cache as many rows that will fit into this much memory for each C<SELECT>.

答案 1 :(得分:0)

增加trace level,并在Perl和GDB调试器下运行代码。您需要找出过程失控的确切位置。

如果您没有运行相关模块和数据库的最新版本,请考虑您找到已修复的旧错误的可能性。

答案 2 :(得分:0)

就#1而言,我相信它会将整个结果加载到内存中 编辑:我记得这是DBI中的一个选项

对于#2和#3,您应该将变量本地化到它们所使用的范围。

我怀疑你执行后实际上已经没有内存了,不过我知道你说不然。您似乎不太可能在该循环中耗尽大量内存。当然除非ROWOUT实际上是对内存中变量的引用,但我们不知道如果你没有提供完整的脚本。