Perl DBI内存泄漏问题

9

我在运行一个perl脚本时遇到了一些内存泄漏问题,因为perl占用的内存量不断增加。因此,我正在尝试使用Devel::Leak来追踪泄漏。我发现每当我调用DBIprepare方法时,Devel::Leak返回的标量值数量就会增加一个。下面是我编写的测试脚本:

#!/usr/bin/perl
use strict;
use Devel::Leak;
use DBI;

START:
my $handle; # apparently this doesn't need to be anything at all
my $leaveCount = 0;
my $enterCount = Devel::Leak::NoteSV($handle);
print "ENTER: $enterCount SVs\n";
{
    # CONFIG VARIABLES
    my $platform = "mysql";
    my $database = "db";
    my $host = "localhost";
    my $port = "3306";
    my $user = "user";
    my $pw = "pass";

    #DATA SOURCE NAME
    my $dsn = "dbi:mysql:$database:$host:3306";

    # PERL DBI CONNECT
    my $dbh = DBI->connect($dsn, $user, $pw);
    $dbh->prepare("SELECT * FROM table"); # The script seems to gain one SV without this
                                          # line here, but since this is my issue in my
                                          # main script I decided to leave it in
    # undef $dbh; I tried undef-ing this, but it made no difference
}
$leaveCount = Devel::Leak::CheckSV($handle);
print "\nLEAVE: $leaveCount SVs\n";
sleep(1);
goto START;

这里我做错了什么,还是DBI模块存在内存泄漏?另外,我知道每次循环添加一个SV并不是一个大问题,而且我可能在其他地方有更大的内存泄漏,导致perl占用服务器的内存如此之多。然而,如果可能的话,我仍然想修复它。程序员的好奇心 :)
更新:第一次似乎会添加约3,000个SV,然后每次都会增加1个。

嗯...这很有趣。我在两台不同的服务器上运行了它,得到了相同的问题。我想知道是否可能是DBI版本的问题? - srchulo
你尝试过 $dbh->disconnect 吗? - ugexe
是的,似乎没有任何作用。我找到了这个链接:http://bugs.mysql.com/bug.php?id=41139,它似乎表明,如果我使用DBI的`prepare_cached`而不是`prepare`,它就会修复它。我试过了,第一次调用prepare时,它最初从77452个SV跳到77482个SV,但然后保持在77482个SV。所以虽然我猜这个“有效”,但我更喜欢像你的那样工作,没有任何不一致之处。 - srchulo
我不认为这可能是问题所在,但是使用启用多线程的 Perl 编译版本(例如,线程化的 Perl)和未启用的版本之间是否有区别?DBD::mysql 是否编译为使用线程安全的 MySQL 客户端库? - ysth
1
跳转语句出了什么问题? - Schwern
显示剩余16条评论
1个回答

5

在$DBI::lasth这个变量中存储了一个DBI::dr实例(一个加强版的哈希表),可以查看ChildHandles键。

#!/usr/bin/perl

use strict;
use warnings;
use Devel::Leak;
use Data::Dumper;
use Symbol::Table;
use DBI;

START:
{
    my $handle;
    my $enterCount = Devel::Leak::NoteSV($handle);

    DB:
    {
        my $platform = "mysql";
        my $database = "db";
        my $host     = "localhost";
        my $port     = "3306";
        my $user     = "user";
        my $pw       = "pass";

        my $dsn = "dbi:mysql:$database:$host:3306";

        my $dbh = DBI->connect( $dsn, $user, $pw );

        $dbh->prepare("SELECT * FROM table");

        $dbh->disconnect();
    }

    my $st = Symbol::Table->New( 'SCALAR', 'DBI' );

    for my $subpkg ( keys %{ $st } ) {

        my $val;
        {
            my $var = "DBI::${subpkg}";
            no strict 'refs';
            $val = ${$var};
        }

        print "scalar '$subpkg' => '$val'\n";
    }

    print Dumper( $DBI::lasth );

    $DBI::lasth->{ChildHandles} = []; # <-- reset leaking data structure

    my $leaveCount = Devel::Leak::CheckSV($handle);

    print "\nCOUNT: $enterCount to $leaveCount SVs\n";

    sleep(1);

    redo START;
}

1
这个修复了内存泄漏问题:$DBI::lasth->{ChildHandles} = []; - ddoxey
1
哇,好棒的侦探工作!我得记住这些模块,下次遇到类似的问题时可以用上。非常感谢! - srchulo
请注意,ChildHandles数组保存的是弱引用,并且“不时地”旧插槽会被释放。如果您不熟悉DBI内部缓存,这并不是泄漏。您可以放心,如果DBI确实存在真正的泄漏,a)将会影响很多人,b)它将会很快得到修复。 - Tim Bunce

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接