Search code examples
perlperlguts

Is the value returned by refaddr permanent?


According to Scalar::Util's documentation, refaddr works like this:

my $addr = refaddr( $ref );

If $ref is reference the internal memory address of the referenced value is returned as a plain integer. Otherwise undef is returned.

However, this doesn't tell me if $addr is permanent. Could the refaddr of a reference change over time? In C, for example, running realloc could change the location of something stored in dynamic memory. Is this analogous for Perl 5?

I'm asking because I want to make an inside-out object, and I'm wondering whether refaddr($object) would make a good key. It seems simplest when programming in XS, for example.


Solution

  • First of all, don't reinvent the wheel; use Class::InsideOut.


    It is permanent. It must be, or the following would fail:

    my $x;
    my $r = \$x;
    ... Do something with $x ...
    say $$r;
    

    Scalars have a "head" at a fixed location. If the SV needs an upgrade (e.g. to hold a string), it's a second memory block known as the "body" that will change. The string buffer is yet a third memory block.

    Layout of an SVt_PVIV scalar

    $ perl -MDevel::Peek -MScalar::Util=refaddr -E'
       my $x=4;
       my $r=\$x;
       say sprintf "refaddr=0x%x", refaddr($r);
       Dump($$r);
       say "";
    
       say "Upgrade SV:";
       $x='abc';
       say sprintf "refaddr=0x%x", refaddr($r);
       Dump($$r);
       say "";
    
       say "Increase PV size:";
       $x="x"x20;
       say sprintf "refaddr=0x%x", refaddr($r);
       Dump($$r);
    '
    

    refaddr=0x2e1db58
    SV = IV(0x2e1db48) at 0x2e1db58             <-- SVt_IV variables can't hold strings.
      REFCNT = 2
      FLAGS = (PADMY,IOK,pIOK)
      IV = 4
    
    Upgrade SV:
    refaddr=0x2e1db58
    SV = PVIV(0x2e18b40) at 0x2e1db58           <-- Scalar upgrade to SVt_PVIV.
      REFCNT = 2                                    New body at new address,
      FLAGS = (PADMY,POK,IsCOW,pPOK)                but head still at same address.
      IV = 4
      PV = 0x2e86f20 "abc"\0                    <-- The scalar now has a string buffer.
      CUR = 3
      LEN = 10
      COW_REFCNT = 1
    
    Increase PV size:
    refaddr=0x2e1db58
    SV = PVIV(0x2e18b40) at 0x2e1db58
      REFCNT = 2
      FLAGS = (PADMY,POK,pPOK)
      IV = 4
      PV = 0x2e5d7b0 "xxxxxxxxxxxxxxxxxxxx"\0   <-- Changing the address of the string buffer
      REFCNT = 2                                    doesn't change anything else.
      CUR = 20
      LEN = 22