Perl:在没有超出范围的情况下,何时不需要释放标量的内存?

时间:2016-09-15 14:10:38

标签: perl memory memory-management

我有一个应用程序,它将大量的文本数据读入标量,有时甚至是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 GB,我提取了4 GB的数据并替换了提取的数据。第二行现在将报告,例如100 MB的数据作为字符串的长度,但是例如Devel::Size::total_size仍将输出为该标量分配的5 GB数据。将undef或类似内容分配给$$contentRef似乎没有改变这一点,我需要将undef作为该标量的函数调用。

我原本预计在$$contentRef应用后,substr后面的内存已经至少部分释放了。似乎并非如此......

那么,如果变量超出范围,是否只释放内存?如果是这样,为什么分配undef与调用undef不同,作为同一标量的函数?

1 个答案:

答案 0 :(得分:13)

您的分析是正确的。

$ 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's 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.