Подтвердить что ты не робот

Perl: Когда ненужная память скаляра освобождается без выхода из области видимости?

У меня есть приложение, которое читает гигантский кусок текстовых данных в скаляр, иногда даже в формате GB. Я использую substr на этом скаляре, чтобы прочитать большую часть данных в другом скаляре и заменить извлеченные данные пустой строкой, потому что это больше не нужно в первом скаляре. Недавно я обнаружил, что Perl не освобождает память первого скаляра, в то время как он распознает, что его логическая длина изменилась. Итак, что мне нужно сделать, это снова извлечь данные из первого скаляра в третий, undef первый скаляр и вернуть извлеченные данные на место. Только так память, занятая первым скаляром, действительно освобождается. Присваивание undef этому скаляру или другому значению, меньшему, чем выделенный блок памяти, ничего не меняет в распределенной памяти.

Теперь я делаю следующее:

     $$extFileBufferRef = substr($$contentRef, $offset, $length, '');
     $length            = length($$contentRef);
  my $content           = substr($$contentRef, 0, $length);
     $$contentRef       = undef( $$contentRef) || $content;

$$contentRef может быть, например, 5 ГБ в первой строке, я извлекаю 4,9 ГБ данных и заменяю извлеченные данные. Вторая строка будет теперь отчитываться, например. 100 МБ данных как длина строки, но, например, Devel::Size::total_size все равно выдаст, что для этого скаляра выделено 5 ГБ данных. И присваивание undef или такому $$contentRef, похоже, ничего не меняет, мне нужно вызвать undef как функцию на этом скаляре.

Я бы предположил, что память за $$contentRef уже по крайней мере частично освобождена после применения substr. Кажется, не так...

Итак, освобождается ли память, если переменные выходят за рамки? И если да, то почему присвоение undef отличается от вызова undef как функции на том же скаляре?

4b9b3361

Ответ 1

Ваш анализ верен.

$ perl -MDevel::Peek -e'
   my $x; $x .= "x" for 1..100;
   Dump($x);
   substr($x, 50, length($x), "");
   Dump($x);
'
SV = PV(0x24208e0) at 0x243d550
  ...
  CUR = 100       # length($x) == 100
  LEN = 120       # 120 bytes are allocated for the string buffer.

SV = PV(0x24208e0) at 0x243d550
  ...
  CUR = 50        # length($x) == 50
  LEN = 120       # 120 bytes are allocated for the string buffer.

Не только строчные строки Perl, но даже свободные переменные, выходящие за пределы области видимости, не используют их повторно при следующем вводе области.

$ perl -MDevel::Peek -e'
   sub f {
      my ($set) = @_;
      my $x;
      if ($set) { $x = "abc"; $x .= "def"; }
      Dump($x);
   }

   f(1);
   f(0);
'
SV = PV(0x3be74b0) at 0x3c04228   # PV: Scalar may contain a string
  REFCNT = 1
  FLAGS = (POK,pPOK)              # POK: Scalar contains a string
  PV = 0x3c0c6a0 "abcdef"\0       # The string buffer
  CUR = 6
  LEN = 10                        # Allocated size of the string buffer

SV = PV(0x3be74b0) at 0x3c04228   # Could be a different scalar at the same address,
  REFCNT = 1                      #   but it truly the same scalar
  FLAGS = ()                      # No "OK" flags: undef
  PV = 0x3c0c6a0 "abcdef"\0       # The same string buffer
  CUR = 6
  LEN = 10                        # Allocated size of the string buffer

Логика заключается в том, что если вам понадобится память один раз, есть еще один шанс, который вам понадобится снова.

По той же причине назначение undef скаляру не освобождает свой строковый буфер. Но Perl дает вам возможность освобождать буферы, если вы хотите, поэтому передача скаляра в undef заставляет освобождать скалярные внутренние буферы.

$ perl -MDevel::Peek -e'
   my $x = "abc"; $x .= "def";  Dump($x);
   $x = undef;                  Dump($x);
   undef $x;                    Dump($x);
'
SV = PV(0x37d1fb0) at 0x37eec98   # PV: Scalar may contain a string
  REFCNT = 1
  FLAGS = (POK,pPOK)              # POK: Scalar contains a string
  PV = 0x37e8290 "abcdef"\0       # The string buffer
  CUR = 6
  LEN = 10                        # Allocated size of the string buffer

SV = PV(0x37d1fb0) at 0x37eec98   # PV: Scalar may contain a string
  REFCNT = 1
  FLAGS = ()                      # No "OK" flags: undef
  PV = 0x37e8290 "abcdef"\0       # The string buffer is still allcoated
  CUR = 6
  LEN = 10                        # Allocated size of the string buffer

SV = PV(0x37d1fb0) at 0x37eec98   # PV: Scalar may contain a string
  REFCNT = 1
  FLAGS = ()                      # No "OK" flags: undef
  PV = 0                          # The string buffer has been freed.