perl: Почему Devel::Refcount::refcount и Devel::Peek::SvREFCNT расходятся во мнениях?

Я читал Как я могу получить доступ к счетчику ссылок хеша Perl?, и там предлагаются как Devel::Refcount::refcount, так и Devel::Peek::SvREFCNT.

Но они не возвращают одинаковые счетчики ссылок. Это почему?

Вот модифицированный пример из perldoc Devel::Refcount:

use Devel::Peek;
use Devel::Refcount;

my $anon = [];

printf "Anon ARRAY $anon has %d/%d reference\n",
    Devel::Refcount::refcount($anon),
    Devel::Peek::SvREFCNT($anon);

my $otherref = $anon;

printf "Anon ARRAY $anon now has %d/%d references\n",
    Devel::Refcount::refcount($anon),
    Devel::Peek::SvREFCNT($anon);

который распечатывает:

Anon ARRAY ARRAY(0x8b10818) has 1/1 reference
Anon ARRAY ARRAY(0x8b10818) now has 2/1 references

Обратите внимание на последнее несоответствие 2/1...

(Если выяснится, что я не делаю глупостей, я добавлю ссылку из Как получить доступ к счетчику ссылок хэша Perl? сюда)


person Peter V. Mørch    schedule 05.07.2012    source источник


Ответы (2)


Я не могу сказать, что я все это уже понял, но ответ на ваш вопрос хорошо виден в Devel::Refcount perldoc

СРАВНЕНИЕ С SvREFCNT

Эта функция отличается от Devel::Peek::SvREFCNT тем, что SvREFCNT() возвращает счетчик ссылок на сам объект SV, на который он передается, тогда как refcount() дает счетчик объекта, на который указывает указатель. Это позволяет также подсчитывать любой референт (например, типы ARRAY, HASH, CODE, GLOB и Regexp).

Рассмотрим следующий пример программы:

 use Devel::Peek qw( SvREFCNT );
 use Devel::Refcount qw( refcount );

 sub printcount
 {
    my $name = shift;

    printf "%30s has SvREFCNT=%d, refcount=%d\n",
       $name, SvREFCNT($_[0]), refcount($_[0]);
 }

 my $var = [];

 printcount 'Initially, $var', $var;

 my $othervar = $var;

 printcount 'Before CODE ref, $var', $var;
 printcount '$othervar', $othervar;

 my $code = sub { undef $var };

 printcount 'After CODE ref, $var', $var;
 printcount '$othervar', $othervar;

Это производит вывод

            Initially, $var has SvREFCNT=1, refcount=1
      Before CODE ref, $var has SvREFCNT=1, refcount=2
                  $othervar has SvREFCNT=1, refcount=2
       After CODE ref, $var has SvREFCNT=2, refcount=2
                  $othervar has SvREFCNT=1, refcount=2

Здесь мы видим, что SvREFCNT() подсчитывает количество ссылок на объект SV, переданных в виде скалярного значения — $var или $othervar соответственно, тогда как refcount() подсчитывает количество ссылок, указывающих на референтный объект — анонимный МАССИВ в этом случае.

До создания ссылки CODE обе ​​переменные $var и $othervar имеют значение SvREFCNT(), равное 1, поскольку они существуют только в текущем лексическом блоке. Анонимный ARRAY имеет значение refcount(), равное 2, потому что и $var, и $othervar хранят ссылку на него.

После создания ссылки CODE переменная $var теперь имеет SvREFCNT(), равную 2, поскольку она также появляется в лексическом блоке для нового анонимного блока CODE.

person mob    schedule 05.07.2012
comment
О, это было неловко! РТФМ! Спасибо. - person Peter V. Mørch; 05.07.2012

Devel::Refcount::refcount($anon) возвращает счетчик ссылок того, на что ссылается $anon.

На массив ссылаются $anon и $otherref: 2


Devel::Peek::SvREFCNT($anon) возвращает счетчик ссылок самого $anon.

На скаляр ссылается блокнот, в котором он находится: 1

Devel::Peek, похоже, не предоставляет средств для получения количества ссылок на массивы, хэши и т.д.


$ perl -MDevel::Peek -E'my $aref2 = my $aref1 = []; Dump($aref1);'
SV = IV(0x99eee34) at 0x99eee38
  REFCNT = 1                    <---- Devel::Peek::SvREFCNT
  FLAGS = (PADMY,ROK)
  RV = 0x99d57d0
  SV = PVAV(0x99d6778) at 0x99d57d0
    REFCNT = 2                  <---- Devel::Refcount::refcount
    FLAGS = ()
    ARRAY = 0x0
    FILL = -1
    MAX = -1
    ARYLEN = 0x0
    FLAGS = (REAL)

Perl предоставляет частично поддерживаемую встроенную функцию Internals::SvREFCNT, которая работает со скалярами, массивами и хэшами.

Internals::SvREFCNT(@$anon) возвращает счетчик ссылок самого @$anon.

На массив ссылаются $anon и $otherref: 2

Вышеупомянутое работает только для скаляров, массивов и хэшей, и вам нужно использовать правильный сигил. Если вы просто хотите передать произвольную ссылку, вы можете использовать:

&Internals::SvREFCNT($anon) + 1 возвращает счетчик ссылок того, на что ссылается $anon.

На массив ссылаются $anon и $otherref: 2

person ikegami    schedule 05.07.2012
comment
Добавлено упоминание встроенного Internals::SvREFCNT. - person ikegami; 05.07.2012
comment
Верно. То, что Devel::Peek не может подсчитывать МАССИВЫ, ХЕШИ и т. д., является причиной, по которой я написал Devel::Refcount в первую очередь. - person LeoNerd; 15.07.2012