Perl 内存使用分析和泄漏检测?

Posted

技术标签:

【中文标题】Perl 内存使用分析和泄漏检测?【英文标题】:Perl memory usage profiling and leak detection? 【发布时间】:2010-11-24 11:47:20 【问题描述】:

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

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

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

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

我不习惯在 Perl 调试器或任何各种交互式分析器中花费时间,因此,我会很感激您给出热情、温和、不深奥的回应。 :-)

【问题讨论】:

你弄明白了吗?鉴于您提供的信息,我最好的猜测是有一个库(通过某些模块的 dynaloader 引入)是罪魁祸首...... 这似乎已成为典型的“寻找内存泄漏”问题,因为我对其他类似问题的回答都已合并到这里 :) 我实际上并没有回答一个问题三遍;随着时间的推移,多个线程已合并在一起。 口误......你的意思是“驻留集大小”......这个数字与堆栈无关 【参考方案1】:

你可以在你的一个对象中有一个循环引用。当垃圾收集器来释放这个对象时,循环引用意味着该引用所引用的所有内容都不会被释放。您可以使用Devel::Cycle 和Test::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

【讨论】:

附言。您可以使用 Scalar::Util::weaken() 削弱现有引用(以允许析构函数通过循环发挥其魔力) -- search.cpan.org/~gbarr/Scalar-List-Utils-1.21/lib/Scalar/… 嗨,Ether - 尝试了 UNIVERSAL::DESTROY(),运行了很长一段时间的服务并敲了它,但没有得到任何东西。与此同时,RSS 正在悄然兴起。如果不是循环引用的问题,它还能是什么? 我真的不确定在析构函数中寻找内存泄漏对你有什么好处。如果您正在泄漏,则永远不会调用 DESTROY。 @Ether:非常感谢。我使用了您的代码,它在我的代码中检测到一些循环引用。现在的问题是我无法找到创建循环引用的确切位置(因为代码有数千行)。 Devel::Cycle 可以打印精确的行吗? [输出:Circular reference found while destroying object of type ActiveState::Config::Node! reftype: ARRAY]【参考方案2】:

您可以使用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 分配给它以释放它指向的任何内容)。

【讨论】:

变量在示例中的使用不一致,例如$leave vs $leaveCount【参考方案3】:

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

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

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

【讨论】:

【参考方案4】:

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

【讨论】:

【参考方案5】:

Perl 手册中包含一个很好的指南:Debugging Perl memory usage

【讨论】:

以上是关于Perl 内存使用分析和泄漏检测?的主要内容,如果未能解决你的问题,请参考以下文章

Android内存优化三:内存泄漏检测与监控

使用UMDH检测内存泄漏

C的内存泄漏检测器?

检测内存泄漏

使用 Visual Studio 2010 检测内存泄漏?

使用 Android Studio 检测内存泄漏与解决内存泄漏问题