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 内存使用分析和泄漏检测?的主要内容,如果未能解决你的问题,请参考以下文章