3

我正在尝试做一个与此等效的 XS:

package RefTestPP;
use strict;
use warnings;

sub new {
    my ($class, $self) = (@_, {});
    return bless $self, $class;
}

1;

这种构造函数应该在调用时“自动激活”它的基础RefTestPP->new(),或者使用给定的引用作为基础,比如RefTestPP->new({ stuff => 123 });.

但是,我遇到了无法解释的泄漏。这是我的RefTest.xs文件:

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"

MODULE = RefTest PACKAGE = RefTest

PROTOTYPES: ENABLE

void
new(sclass="RefTest", base=sv_2mortal(newRV_noinc((SV *) newHV())))
    const char *sclass
    SV *base
    PREINIT:
        HV *stash;
    PPCODE:
        stash = gv_stashpv(sclass, 0);
        ST(0) = sv_bless(base, stash);

        XSRETURN(1);

void
new_leaky(sclass="RefTest", base=newRV_noinc(sv_2mortal((SV *) newHV())))
    const char *sclass
    SV *base
    PREINIT:
        HV *stash;
    PPCODE:
        stash = gv_stashpv(sclass, 0);
        ST(0) = sv_bless(base, stash);

        XSRETURN(1);

RefTest.t以及检测泄漏的文件:

use strict;
use warnings;

use Devel::Leak;
use Test::More;
BEGIN { use_ok('RefTest') };

sub test_leak (&$;$) {
    my ($code, $descr, $maxleak) = (@_, 0);
    my $n1 = Devel::Leak::NoteSV(my $handle);
    $code->() for 1 .. 1000;
    my $n2 = Devel::Leak::CheckSV($handle);
    cmp_ok($n1 + $maxleak, '>=', $n2, $descr);
}

# OK
test_leak { my $ref = RefTest->new() or die }
    'first sv_2mortal(); then newRV_noinc()', 2;

# also OK
test_leak { my $ref = RefTest->new_leaky({}) or die }
    'first sv_2mortal(); then newRV_noinc(); pre-init base', 2;

# leaks!
test_leak { my $ref = RefTest->new_leaky() or die }
    'first newRV_noinc(); then sv_2mortal()', 2;

done_testing 4;

(正确编译所需的其余文件是由 生成的默认文件h2xs -A -n RefTest

重点是:

  1. 作为参数传递给构造函数的基引用永远不会泄漏;
  2. XS 代码中创建的基础sv_2mortal(newRV_noinc((SV *) newHV()))也不会泄漏;
  3. 使用创建的 basenewRV_noinc(sv_2mortal((SV *) newHV()))将泄漏(并最终Attempt to free unreferenced scalar: SV 0xdeadbeef在全局销毁期间导致臭名昭著的消息)。

sv_2mortal(newRV_noinc(...))有什么不同的理由newRV_noinc(sv_2mortal(...))吗?我只是做错了吗?

4

1 回答 1

5

sv_2mortal是延迟的引用计数减量。(它发生在调用者有机会引用它或复制它之后。)在这篇文章中,我将给出 ref 计数,就好像sv_2mortal立即递减一样,但会使用星号(“*”)来表示这一点。


以下代码使引用死亡:

sv_2mortal(newRV_noinc((SV*)newHV()))

所以,

  1. 哈希的 REFCNT=1。这很好,因为您创建的引用包含对它的引用。
  2. 参考具有 REFCNT=0*。这很好,因为没有任何引用它。

以下代码使哈希死亡:

newRV_noinc(sv_2mortal((SV*)newHV()))

所以,

  1. 哈希有 REFCNT=0*。这很糟糕,因为您创建的引用包含对它的引用。这将导致过早释放(这就是为什么在释放引用时会出现“尝试释放未引用的标量”的原因)。
  2. 参考具有 REFCNT=1。这很糟糕,因为没有任何东西引用它。这是一个泄漏。
于 2012-11-27T02:18:36.423 回答