34

我用 Perl 编写了一个在 Linux 上运行的持久网络服务。

不幸的是,当它运行时,它的驻留堆栈大小 (RSS) 只会增长,增长,增长,缓慢而稳定。

尽管我努力消除所有不需要的哈希键并删除对对象的所有引用,否则这些引用会导致引用计数保持在原位并阻碍垃圾收集。

是否有任何好的工具可以分析与 Perl 程序中的各种本机数据原语、祝福哈希引用对象等相关的内存使用情况?你用什么来追踪内存泄漏?

我不习惯在 Perl 调试器或任何各种交互式分析器中花费时间,因此,我将不胜感激。:-)

4

5 回答 5

15

您可以在其中一个对象中有循环引用。当垃圾收集器来释放这个对象时,循环引用意味着该引用所引用的所有内容都不会被释放。您可以使用Devel::CycleTest::Memory::Cycle检查循环引用。要尝试的一件事(尽管它在生产代码中可能会变得昂贵,所以当未设置调试标志时我会禁用它)是检查析构函数内的所有对象的循环引用:

# make this be the parent class for all objects you want to check;
# or alternatively, stuff this into the UNIVERSAL class's destructor
package My::Parent;
use strict;
use warnings;
use Devel::Cycle;   # exports find_cycle() by default

sub DESTROY
{
    my $this = shift;

    # callback will be called for every cycle found
    find_cycle($this, sub {
            my $path = shift;
            foreach (@$path)
            {
                my ($type,$index,$ref,$value) = @$_;
                print STDERR "Circular reference found while destroying object of type " .
                    ref($this) . "! reftype: $type\n";
                # print other diagnostics if needed; see docs for find_cycle()
            }
        });

    # perhaps add code to weaken any circular references found,
    # so that destructor can Do The Right Thing
}
于 2009-09-01T01:19:33.547 回答
10

您可以使用Devel::Leak搜索内存泄漏。但是,文档非常稀少……例如,从哪里获得 $handle 引用以传递给Devel::Leak::NoteSV()如果我找到答案,我将编辑此回复。

好的,事实证明使用这个模块非常简单(代码从Apache::Leak无耻地窃取):

use Devel::Leak;

my $handle; # apparently this doesn't need to be anything at all
my $leaveCount = 0;
my $enterCount = Devel::Leak::NoteSV($handle);
print STDERR "ENTER: $enterCount SVs\n";

#  ... code that may leak

$leaveCount = Devel::Leak::CheckSV($handle);
print STDERR "\nLEAVE: $leaveCount SVs\n";

我会在中间部分放置尽可能多的代码,让 leaveCount 检查尽可能接近执行结束(如果有的话)——在大多数变量尽可能被释放之后(如果你不能得到一个超出范围的变量,您可以将 undef 分配给它以释放它指向的任何内容)。

于 2009-09-01T01:23:15.820 回答
4

接下来要尝试什么(不确定这是否最好放在 Alex 的上述问题之后的评论中):我接下来要尝试什么(Devel::Leak 除外):

尝试消除程序中“不必要的”部分,或将其分割成单独的可执行文件(它们可以使用信号进行通信,或者可能使用命令行参数相互调用)——目标是将可执行文件简化为最小的数量仍然表现出不良行为的代码。如果您确定不是您的代码在执行此操作,请减少您正在使用的外部模块的数量,尤其是那些具有 XS 实现的模块。如果它可能是您自己的代码,请寻找任何可能可疑的内容:

  • 绝对使用 Inline::C 或 XS 代码
  • 直接使用引用,例如\@listor \%hash,而不是像 [ qw(foo bar) ] 这样的预分配引用(前者会创建另一个可能会丢失的引用;在后者中,只需担心一个引用,它通常存储在局部词汇标量
  • 间接地操作变量,例如$$foowhere$foo被修改,这可能导致变量自动激活(尽管您需要禁用strict 'refs'检查)
于 2009-09-01T03:18:24.433 回答
3

我最近使用NYTProf作为大型 Perl 应用程序的分析器。它不会跟踪内存使用情况,但会跟踪所有已执行的代码路径,这有助于找出泄漏的来源。如果您泄漏的是数据库连接等稀缺资源,那么跟踪它们的分配和关闭位置对于查找泄漏有很大帮助。

于 2010-09-14T18:49:56.197 回答
2

Perl 手册中有一个很好的指南:调试 Perl 内存使用

于 2009-11-03T17:45:23.057 回答