【发布时间】:2014-09-17 15:34:25
【问题描述】:
首先让我说我对 Perl 没有深入的了解,所以如果我遗漏了一些明显的东西,请原谅我:)
在我正在查看的系统(在 Windows 环境中运行)中,我们有一个 perl 进程,它必须下载 ~5000-6000 个文件。由于每个文件都可以独立下载,我们为每个文件分叉了单独的线程。该线程应该下载文件并死亡。在运行该进程时,我注意到该进程的内存上升到 ~1.7 GB,然后由于每个进程的内存限制而死掉。
在搜索和询问一些人时,我遇到了循环引用的概念,因为垃圾收集器不会释放内存。我搜索了一下,找到了Devel-Cycle 包,它可以找出对象中是否有任何循环。我得到了这个包并添加了一行来检查进程中的主要对象是否有任何循环。 find_cycle 为每个线程返回了以下语句。
DBD::Oracle::db FIRSTKEY failed: handle 2 is owned by thread 256004 not current thread c0ea29c (handles can't be shared between threads and your driver may need a CLONE method added) at C:/Program Files/Perl/site/lib/Devel/Cycle.pm line 151.
我知道数据库句柄不能在线程之间共享。我再次查看代码并意识到在 fork 发生后,子进程确实创建了一个新的 DB 句柄(我猜这就是为什么进程仍然继续正常运行直到达到内存限制的原因)。我想对象中可能有更多来自父级的 db 句柄,这些句柄没有被子级使用但仍被引用。
我的问题 -
循环引用是问题的唯一原因,还是有其他问题导致进程使用这么多内存?
句柄的共享是否会导致内存爆炸(换句话说,共享的 DB 句柄导致 GC 无法释放空间)?
如果它确实是共享数据库句柄,我想我可以说
$dbHandle = 0以摆脱引用(如果$dbHabndle正在引用该特定句柄)。我说的对吗?我正在尝试查看代码以查看在其他地方还有对父数据库句柄的引用(并找到至少一个更多引用)。我还有其他方法可以做到这一点吗?有没有办法打印出一个对象的所有属性?
编辑: 并非所有线程(由于 windows 中的 perl fork 调用)都是同时产生的。它产生最多 n 个线程(其中 n 是可配置的数字)。一旦一个线程完成了它的执行,这个进程就会产生另一个线程。此时 n 设置为 10,但是我已将 n 更改为 1(因此一次只运行一个额外的线程),但我仍然达到了内存限制。
【问题讨论】:
-
你的代码是什么样的?你使用什么模块?你如何使用它们?需要更多详细信息。
-
@TLP - 我只提到了我认为导致问题的过程的主要部分。它是一个更大系统的一部分,该系统使用大量模块,如 DBD::Oracle、XML::Simple、Net::FTP、File::Path 等。
-
您在使用数据库句柄后是否断开连接?
-
循环引用是在 Perl 中造成内存泄漏的主要方式之一——它使用引用计数来检测内存是否可能被释放。循环引用永远不会降为零,因此它永远不会释放。
-
@TLP - 查看代码,我认为现在不会发生这种情况。让我再次检查并修复它,如果确实如此。但是,这会导致内存爆炸吗?一旦线程完成并因此收集垃圾,句柄对象的引用是否应该超出范围。
标签: multithreading perl garbage-collection