如何在Perl CGI脚本中调试可能的内存泄漏?

时间:2010-01-28 17:57:26

标签: perl apache memory cgi

我有一个在Apache上运行的遗留Perl CGI页面,它处理大量的Excel电子表格,并根据需要添加到数据库中。它在数据组中处理,每组数据都被发送到数据库。

每次调用数据库后,我的系统可用内存会显着减少到没有内存的程度。一旦我最终得到“脚本标头过早结束”错误并将HTTP代码500返回给客户端,内存将被释放回系统。

查看(复杂)代码,我无法找到可能发生内存泄漏的位置。是否有一些技巧或工具可以用来确定内存的去向?

3 个答案:

答案 0 :(得分:2)

简短的回答是,你很难受。没有一个很好的,随时可用的程序,您可以运行以获得答案。对不起,我无法提供更多的帮助,但没有看到任何代码等,没有任何人能给出的更好的建议。

我无法谈论你的具体情况,但这里有一些我过去做过的事情。找出造成问题的一般区域非常重要。它与其他调试技术没什么不同。通常我发现这些东西没有优雅的解决方案。你只需卷起袖子,将手臂肘部深深地粘在泥土中,无论它有多么难闻。

首先,在Web服务器外部运行程序。如果你仍然从命令行看到问题,请高兴:你(大多数时候)只是排除了Web服务器的问题。这可能需要一些工作来制作一个包装器脚本来设置Web环境,但它最终会变得更加容易,因为您不需要重新启动服务器等来重置环境。

如果您无法在服务器外部复制问题,您仍然可以按照我的建议做下一步,这会更烦人。如果这是一个Web服务器问题而不是命令行中的问题,则该任务将成为关于这两者之间差异的发现。我遇到过这种情况。

如果Web服务器不是问题,请开始将脚本二等分,就像调试问题一样。如果启用了日志记录,请在记录其实际内存使用情况时将其打开并观看程序运行。什么时候爆炸?听起来你已经缩小到一些数据库调用。如果你能够从命令行或调试器运行它,我会在内存增加之前和之后找到一对合适的断点,并逐渐将它们放在一起。您可以使用Devel::Size之类的模块来查看您怀疑的数据结构的内存大小。

从那里开始,它只是缩小了嫌疑人的范围。找到嫌疑人后,看看您是否可以在简短的示例脚本中复制它。您希望消除尽可能多的贡献因素。

一旦您认为自己找到了有问题的代码,也许您可​​以提出另一个显示代码的问题,如果您仍然不明白发生了什么。

如果你想要真正的想象,你可以编写自己的Perl调试器。这并不难。您有机会在语句的开头或结尾处的DB命名空间中运行一些子例程。你有你怀疑的东西的调试代码列表内存配置文件,并寻找内存大小的跳转。除非其他一切都失败,否则我不会尝试这个。

答案 1 :(得分:-1)

如果问题出在Perl代码中,您可能有一个指向自身或父节点的引用。

以下是展示此行为的快速代码示例。

{
  my @a;
  @a = [\@a];
}

通常它以对象的形式出现,引用父对象。

{ package parent;
  sub new{ bless { 'name' => $_[1] }, $_[0] }
  sub add_child{
    my($self,$child_name) = @_;
    my $child = child->new($child_name,$self);
    $self->{$child_name} = $child;   # saves a reference to the child
    return $child;
  }
}
{ package child;
  sub new{
    my($class,$name,$parent) = @_;
    my $self = bless {
      'name' => $name,
      'parent' => $parent # saves a reference to the parent
    }, $class;
    return $self;
  }
}
{
  my $parent = parent->new('Dad');
  my $child  = parent->add_child('Son');

  # At this point both of these are true
  # $parent->{Son}{parent} == $parent
  # $child->{parent}{Son}  == $child

  # Both of the objects **would** be destroyed upon leaving
  # the current scope, except that the object is self-referential
}

# Both objects still exist here, but there is no way to access either of them.

解决此问题的最佳方法是使用Scalar::Util::weaken

use Scalar::Util qw'weaken';
{ package child;
  sub new{
    my($class,$name,$parent) = @_;
    my $self = bless {
      'name' => $name,
      'parent' => $parent
    }, $class;

    weaken ${$self->{parent}};

    return $self;
  }
}

我建议尽可能从孩子那里删除父对象的引用。

答案 2 :(得分:-1)

您是如何写入数据库的?如果您正在使用任何DBI包或自定义包装器,请确保您正在刷新所有缓存对象或缓存变量。这些类型的内存膨胀问题相对常见,通常代表一个继续存在的共享对象缓存。

要尝试的事情:

  • 完成后清除对象变量
  • 深度循环数据库连接(这是极端的,但根据您的连接方式,它可能会解决问题)。
  • 一次只加载一组数据,并刷新任何变量或工厂对象在组之间加载它。

希望有所帮助。