【问题标题】:How to properly tie a hash with DBM::Deep to avoid memory leaks?如何正确地将哈希与 DBM::Deep 绑定以避免内存泄漏?
【发布时间】:2015-04-15 19:33:32
【问题描述】:

我正在对一些非常大的数据进行图形分析,我需要存储一组特定图形边的所有分数。鉴于数据的大小,我需要将信息写入磁盘,并且我正在尝试使用带有DBM::Deep 的绑定哈希。这是基本设置:

#!/usr/bin/env perl

use 5.010;
use strict;
use warnings;
use autodie;
use File::Spec;
use DBM::Deep;
use Cwd;

my $file = shift;
my $wd = getcwd();

open my $fh, '<', $file;

my %match_pairs;
my $dbm = File::Spec->catfile($wd, "pairs.dbm");
unlink $dbm if -e $dbm;

tie %match_pairs, 'DBM::Deep', { 
    file      => $dbm, 
    locking   => 1, 
    autoflush => 1, 
    type      => DBM::Deep::TYPE_HASH 
};

然后,我解析文件并将信息存储在某个分数阈值以上,如下所示($pair 只是一个常规字符串):

if (exists $match_pairs{$pair}) {
    push @{$match_pairs{$pair}}, $score;
}
else {
    $match_pairs{$pair} = [$score];
}

此代码会产生内存泄漏,该泄漏会增加,直到您终止进程。如果我注释掉这六行,就没有内存泄漏。奇怪的是,数据被写入了 DBM 文件,当我使用 DBM::Deep 或不使用时,我得到了相同的结果,所以看起来 tie 方法是正确的。我更改了日志模式、自动刷新、锁定和其他设置,并且看到了相同的行为。

我在这里错误地使用 DBM::Deep 吗?例如,我应该使用 OO 接口还是有更好的方法来编写这个方法?

我会先发制人地说,很难提供示例文件来重现此问题,因为脚本需要运行几秒钟才能注意到泄漏(这意味着文件必须至少有 100k 行)。我希望有些东西会跳出来,但如果信息不够,我会提供一个脚本和一些数据。我正在使用 Perl v5.20.2 和最新的 DBM::Deep, 2.0011。

编辑:我已将代码简化为:

$match_pairs{$pair} = $score;

我还尝试过使用 OO 接口进行简单的键/值存储,我看到了相同的行为。看来这一定是一个错误,所以我会报告它。

【问题讨论】:

  • 与您的问题完全无关,但您可以删除除push 行之外的所有行并获得相同的结果。
  • @JimDavis 我读到 autovivication 不能与 DBM::Deep 一起正常工作,或者无论如何都不能正常工作,所以我使用 if (exists $hash{key}) { ... } 语法是安全的。
  • @ThisSuitIsBlackNot:tie 返回的值只是一个对象,它具有像哈希一样操作数据库的方法,而绑定哈希只是调用这些方法的媒介。因此,操纵您的$db 只是将虚假数据放入内存对象中,而不会触及数据库;这就是为什么它要快得多。如果需要,您可以使用$db-&gt;STORE($key, $val),但最简单的方法是使用绑定变量并编写$hash{$key} = $val。顺便说一句,正如您所料,禁用 lockingautoflush 也会显着加快速度。
  • @ThisSuitIsBlackNot:是的,文档很差。它的工作方式是 OO 接口在内部执行 tie 并返回对绑定哈希的引用,您可以直接使用它。不,不是真的,虽然我可以看到对于哈希的每 1,000 次推送,该过程有 1,000 个额外的标量变量,但我不知道为什么
  • 我在 github 和 CPAN 错误跟踪器上报告了这个问题。看来一年前在谷歌群组网站上已经有bug report 和讨论,但没有解决方案。这是一个带有简单示例的错误报告的link。与其他报告类似,我没有发现内存周期,但显然某处存在泄漏。

标签: perl dbm


【解决方案1】:

我在相关的 GH 问题中回答了这个问题(感谢您打开它!),但我会在这里重复一遍。

是的,随着时间的推移,RAM 的数量会慢慢增加。当将/usr/dict/words 作为键读入 DBM::Deep 哈希(Ubuntu 14.04,Perl 5.22.0)时,每读取 30k 个字,我的 RAM 就会增加约 1k。我的猜测(没有可重复测试)是这只是 DBM::Deep 需要跟踪添加 30k 元素所需的额外数据级别的簿记。

正如我在本期评论中提到的那样,如果有一个可重复的测试文件我可以提交给项目,我会很高兴进一步研究这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-23
    • 2012-11-30
    • 2019-06-06
    • 2019-05-21
    • 2012-02-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多