发现 Perl 内存泄漏

Posted

技术标签:

【中文标题】发现 Perl 内存泄漏【英文标题】:Finding a Perl memory leak 【发布时间】:2009-11-02 20:30:47 【问题描述】:

已解决见编辑 2

你好,

我一直在编写一个 Perl 程序来处理本地(专有)程序的自动升级(对于我工作的公司)。

基本上,它通过 cron 运行,不幸的是有内存泄漏(或类似的东西)。问题是泄漏只发生在我不看的时候(也就是通过 cron 运行,而不是通过命令行)。

我的代码不包含任何循环(或其他)引用,因此常用的工具对我没有帮助(Devel::CycleDevel::Peek)。

我将如何弄清楚是什么占用了内核杀死它的这么多内存?

基本上,代码 SFTP 进入服务器(使用 ```sftp...` `),调用 OpenSSL 来验证文件,然后如果需要更多文件,则 SFTP 更多,并安装它们(解压缩它们)。

我在第一个 SFTP 会话之前看到了延迟(约 15 秒),但它从未使用过这么多内存以至于被杀死(在我面前)。

如果我不能解决这个问题,我需要用不同的语言重新编写,这将花费宝贵的时间。

编辑:内核打印出以下消息,让我相信这是内存泄漏:

[100023.123] Out of memory: kill process 9568 (update.pl) score 325406 or a child
[100023.123] Killed Process 9568 (update.pl)

我不认为这是 cron 的问题,因为通过命令行运行它时会出现停滞(有时大约 15 秒)。此外,没有使用环境变量(至少根据我所写的,可能是底层的东西呢?)

编辑 2: 在 mobrule 的以下评论的帮助下,我自己发现了这个问题(针对这个问题)。事实证明,该脚本每天仅从用户(非 root)的 crontab 调用一次,并且(非 root 权限)导致了特殊的无限循环情况。

对不起,我觉得以前没有找到这个有点愚蠢,但是谢谢。

mobrule,如果您提交您的评论作为答案,我会接受它,因为它会导致我发现问题。

结束编辑

谢谢, 布赖恩

附:由于公司政策,我可能可以发布少量的 sn-ps 代码,但不能发布全部内容。

【问题讨论】:

当作业在 cron 中的行为与在命令行中不同时,请检查您的环境变量。 我不认为这是 cron 的问题,因为有时当我在命令行上运行它时它会停止超过 15 秒(但不够可靠,无法测试)。此外,cron 每分钟运行一次,因此它有更多的“尝试”来获取脚本中的错误。 抱歉,它只会每分钟运行一次以进行测试,而不是在生产环境中(每周运行一次左右) 好吧,原来我的代码中有一个无限循环(我的错误函数调用了另一个在某些情况下调用错误函数的函数)。它仅在程序未以 root 身份运行时触发(例如从我忘记的 cron 调用时,每天仅调用一次)。 mobrule,如果您提交您的评论作为答案,我会接受它,因为它是导致我找到解决方案的原因。 【参考方案1】:

您可以尝试使用Devel::Size 来分析您的一些对象。例如在main:: 范围内(.pl 文件本身),执行以下操作:

use Devel::Size qw(total_size);

foreach my $varname (qw(varname1 varname2 ))

    print "size used for variable $varname: " . total_size($$varname) . "\n";

将使用的实际大小与您认为每个对象的合理值进行比较。一些可疑的东西可能会立即弹出(例如,一个非常膨胀的缓存,超出了任何听起来合理的范围)。

其他尝试:

一次删除一些功能,看看情况是否突然好转;我会从使用任何外部库开始 不良行为是否仅限于一台特定的机器或特定的操作系统?将程序移至其他系统以查看其行为如何变化。 (在单独的安装中)尝试升级到最新的 Perl (5.10.1),并升级所有 CPAN 模块

【讨论】:

【参考方案2】:

你怎么知道这是内存泄漏?我可以想到操作系统会杀死程序的许多其他原因。

我要问的第一个问题是“这个程序在命令行中总是能正常工作吗?”。如果答案是“否”,那么我会先解决这些问题。

另一方面,如果答案是“是”,我会调查在 cron 下执行程序与从命令行执行程序之间的所有差异,以找出其行为不端的原因。

【讨论】:

我不确定这是否是内存“泄漏”,但它肯定会占用我所有的内存。内核打印此消息:[100023.123] 内存不足:杀死进程 9568 (update.pl) 得分 325406 或子 [100023.123] 杀死进程 9568 (update.pl)。并且程序 100% 的时间(根据我的经验)从命令行运行,但是 cron 每分钟运行一次(用于测试),因此它有更多机会使用所有内存。 反对什么?这是一个非常好的答案。事实上,类似的过程导致 OP 找到了解决他的问题的方法......【参考方案3】:

如果它是由cron运行的,那它不应该在迭代后死掉吗?如果是这样的话,我很难看出内存泄漏会有多大的影响......

您确定是脚本本身,而不是使用内存的子进程吗?也许它最终会创建很多 ssh 会话,而不是在一个会话中做很多事情?

【讨论】:

好吧,内核将以下消息打印到标准错误(所以当我从我正在做的任何事情回来时我都会看到它)[100023.123] 内存不足:杀死进程 9568 (update.pl) 分数325406 或孩子 [100023.123] 杀死进程 9568 (update.pl) 所以这让我相信它是 perl 脚本本身。但无论哪种方式,如果它是子程序之一,我有办法找到它吗?我不能让它泄漏这么多内存的原因是因为视频流服务正在运行并且它必须没有任何宏块(按规范),如果它没有内存,它就有宏块。 看起来这可能是为了防止分叉炸弹,任何有可能你有一些东西可以简化为:fork while fork 不,我唯一拥有的 forks 来自 Perl 的反引号运算符 (`)(如果这甚至是分叉,我不确定) 由于似乎是内存限制问题,ulimit 命令报告的所有参数的硬和软进程限制是什么。还有你用的是什么操作系统? Perl 使用 fork 和 exec 来实现反引号运算符和子进程限制;的环境可能取决于父进程。 (例如在 AIX 上,我曾经在创建孙子进程时分叉失败,因为我有太多子进程!!!)

以上是关于发现 Perl 内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

使用 destroy() 命令时的 Perl/Tk 内存泄漏

如何在长时间运行的 Perl 程序中找到内存泄漏?

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

Android ValueAnimator --内存泄漏

避免、发现和消除 Cocoa 中的内存泄漏

通过tracemalloc模块在python中发现内存泄漏