Gestión de la memoria

En Perl, una variable representa una ligadura lógica entre un nombre y un valor.

Del mismo modo, un array es una colección de ligaduras lógicas a valores escalares. Véase la figura 4.1

Figura 4.1: Un array es una colección de ligaduras lógicas a valores escalares
\begin{figure}\centerline{\epsfig{file=array_view.eps, height=6cm}}\end{figure}

El Contador de Referencias de un Valor

Véase la figura 4.2. Puesto que hay una ligadura entre la variable $a y su valor, este contador es al menos uno. Si este contador desciende hasta cero, Perl elimina la memoria asignada a ese valor.

Los contadores descienden por diversas razones. Por ejemplo, cada vez que termina un bloque, el contador de los valores asociados con las variables declaradas en ese bloque desciende una unidad. En el caso habitual de que el contador valiera 1, pasará a valer 0 y la memoria asociada con el valor será liberada.

Figura 4.2: Contadores de referencia y asignaciones
\begin{figure}\centerline{\epsfig{file=with_counts.ps, height =8cm}}\end{figure}

Ejemplo

El siguiente ejemplo, debido a Ikegami (PerlMonks), está tomado de una respuesta en PerlMonks. Suponga el siguiente código:

1 my $array_ref = [];
2 for(1 .. 2) {
3     my $hash_ref = { foo => foo, bar => bar };
4     push(@{$array_ref}, $hash_ref);
5 }
6 
7 my $hash_ref = $array_ref->[0];
8 $array_ref = [];

Estado después de la línea 7:

 $array_ref                                    $hash_ref
+--------------+                              +--------------+
| array ref    |                              | hash ref     |
+--------------+                              +--------------+
| refcount = 1 |                              | refcount = 1 |
+--------------+    +--------------------+    +--------------+
|           o=====> | array              |    |           o  |
+--------------+    +--------------------+    +---------- : -+    
                    | refcount = 1       |                :
                    +--------------------+                :
                    | element 0          |                :
                    |  +--------------+  |                :
                    |  | hash ref     |  |                :
                    |  +--------------+  |                :
                    |  | refcount = 1 |  |                V
                    |  +--------------+  |    +--------------+
                    |  |           o========> | hash         |
                    |  +--------------+  |    +--------------+
                    +--------------------+    | refcount = 2 |
                    | element 1          |    +--------------+
                    |  +--------------+  |    | keys & vals  |
                    |  | hash ref     |  |    +--------------+
                    |  +--------------+  |
                    |  | refcount = 1 |  |
                    |  +--------------+  |    +--------------+
                    |  |           o========> | hash         |
                    |  +--------------+  |    +--------------+
                    +--------------------+    | refcount = 1 |
                                              +--------------+
                                              | keys & vals  |
                                              +--------------+

Despúes que se sobreescriba la referencia al array (línea 8):

+--------------+                              +--------------+
| array ref    |                              | hash ref     |
+--------------+                              +--------------+
| refcount = 1 |                              | refcount = 1 |
+--------------+    +--------------------+    +--------------+
|           o  |    | array              |    |           o  |
+---------- : -+    +--------------------+    +---------- : -+
            :       | refcount = 0       |                :
            :       +--------------------+                :
            V       | element 0          |                :
+--------------+    |  +--------------+  |                :
| array        |    |  | hash ref     |  |                :
+--------------+    |  +--------------+  |                :
| refcount = 1 |    |  | refcount = 1 |  |                V
+--------------+    |  +--------------+  |    +--------------+
| no elements  |    |  |           o========> | hash         |
+--------------+    |  +--------------+  |    +--------------+
                    +--------------------+    | refcount = 2 |
                    | element 1          |    +--------------+
                    |  +--------------+  |    | keys & vals  |
                    |  | hash ref     |  |    +--------------+
                    |  +--------------+  |
                    |  | refcount = 1 |  |
                    |  +--------------+  |    +--------------+
                    |  |           o========> | hash         |
                    |  +--------------+  |    +--------------+
                    +--------------------+    | refcount = 1 |
                                              +--------------+
                                              | keys & vals  |
                                              +--------------+

El array original es liberado automáticamente puesto que su contador de referencias alcanza cero. Las estructuras de los contadores de los elementos alcanzan cero:

+--------------+                              +--------------+
| array ref    |                              | hash ref     |
+--------------+                              +--------------+
| refcount = 1 |                              | refcount = 1 |
+--------------+                              +--------------+
|           o  |                              |           o  |
+---------- : -+                              +---------- : -+
            :                                             :
            :                                             :
            V                                             :
+--------------+       +--------------+                   :
| array        |       | hash ref     |                   :
+--------------+       +--------------+                   :
| refcount = 1 |       | refcount = 0 |                   V
+--------------+       +--------------+       +--------------+
| no elements  |       |           o========> | hash         |
+--------------+       +--------------+       +--------------+
                                              | refcount = 2 |
                                              +--------------+
                       +--------------+       | keys & vals  |
                       | hash ref     |       +--------------+
                       +--------------+   
                       | refcount = 0 |   
                       +--------------+       +--------------+
                       |           o========> | hash         |
                       +--------------+       +--------------+
                                              | refcount = 1 |
                                              +--------------+
                                              | keys & vals  |
                                              +--------------+

Los contadores de referencias de los hashes alcanzan cero y son liberados. Ahora el contador del antiguo segundo elemento alcanza cero:

+--------------+                              +--------------+
| array ref    |                              | hash ref     |
+--------------+                              +--------------+
| refcount = 1 |                              | refcount = 1 |
+--------------+                              +--------------+
|           o  |                              |           o  |
+---------- : -+                              +---------- : -+
            :                                             :
            :                                             :
            V                                             :
+--------------+                                          :
| array        |                                          :
+--------------+                                          :
| refcount = 1 |                                          V
+--------------+                              +--------------+
| no elements  |                              | hash         |
+--------------+                              +--------------+
                                              | refcount = 1 |
                                              +--------------+
                                              | keys & vals  |
                                              +--------------+
                                             
                                             
                                              +--------------+
                                              | hash         |
                                              +--------------+
                                              | refcount = 0 |
                                              +--------------+
                                              | keys & vals  |
                                              +--------------+

y es liberado:

+--------------+                              +--------------+
| array ref    |                              | hash ref     |
+--------------+                              +--------------+
| refcount = 1 |                              | refcount = 1 |
+--------------+                              +--------------+
|           o  |                              |           o  |
+---------- : -+                              +---------- : -+
            :                                             :
            :                                             :
            V                                             :
+--------------+                                          :
| array        |                                          :
+--------------+                                          :
| refcount = 1 |                                          V
+--------------+                              +--------------+
| no elements  |                              | hash         |
+--------------+                              +--------------+
                                              | refcount = 1 |
                                              +--------------+
                                              | keys & vals  |
                                              +--------------+

Liberación de Memoria en Estructuras de Datos Cíclicas

Existe un problema con este algoritmo de recolección de basura: las referencias circulares. En esos casos el programador debe actuar usando, por ejemplo, el operador delete y las funciones weaken e isweak en el módulo Scalar::Util.

pp2@nereida:~$ perl -wde 0
main::(-e:1):   0
  DB<1> use Scalar::Util qw(weaken isweak)
  DB<2> $var = "hola\n"
  DB<3> $ref = \$var
  DB<4> weaken($ref)
  DB<5> p isweak($ref)
1
  DB<6> $c = $ref
  DB<7> p "<".isweak($c).">"
<>

La función weaken

La función weaken recibe como argumento una referencia. Hace que no se incremente el contador de referencias del valor asociado con dicha referencia. Vea el siguiente ejemplo:



hp@nereida:~/Lperl/src$ cat -n noweaken.pl
1  use strict;
2  use Scalar::Util qw(weaken);
3  my $ref;
4  {
5    my $var = "hola\n";
6    $ref = \$var;
7  }
8  print $$ref;
lhp@nereida:~/Lperl/src$ cat -n weaken.pl
 1  use strict;
 2  use Scalar::Util qw(weaken);
 3  my $ref;
 4  {
 5    my $var = "hola\n";
 6    $ref = \$var;
 7    weaken($ref);
 8  }
 9  print $$ref;
lhp@nereida:~/Lperl/src$ perl noweaken.pl
hola
hp@nereida:~/Lperl/src$ perl weaken.pl
Can't use an undefined value as a SCALAR reference ...


Una referencia se dice débil (weak) si no cuenta en el objeto al que referencia. Cuando el contador del objeto al que referencia alcanza cero la referencia contiene undef.

Tenga en cuenta que la copia de una referencia débil (creada con weaken) es una referencia fuerte:

lhp@nereida:~/Lperl/src$ cat -n weakenwithcopy.pl
 1  use strict;
 2  use Scalar::Util qw(weaken);
 3  my ($ref, $copy);
 4  {
 5    my $var = "hola\n";
 6    $ref = \$var;
 7    weaken($ref);
 8    $copy = $ref;
 9  }
10  print $$ref;
11
lhp@nereida:~/Lperl/src$ perl weakenwithcopy.pl
hola

La función isweak

La función isweak retorna TRUE si la expresión pasada como parámetro es una referencia débil.

Detectando Ciclos

El módulo Devel::Cycle permite la detección de ciclos:
lhp@nereida:~/Lperl/src/testing$ cat -n ciclos.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  use Devel::Cycle;
     4  my $test = {
     5    fred   => [qw(a b c d e)],
     6    ethel  => [qw(1 2 3 4 5)],
     7    george => {martha => 23, agnes  => 19}
     8  };
     9  $test->{george}{phyllis} = $test;
    10  $test->{fred}[3]      = $test->{george};
    11  $test->{george}{mary} = $test->{fred};
    12  find_cycle($test);
    13  exit 0;
lhp@nereida:~/Lperl/src/t
Al ejecutar produce una salida como esta:
lhp@nereida:~/Lperl/src/testing$ ./ciclos.pl
Cycle (1):
                          $A->{'fred'} => \@B
                               $B->[3] => \%C
                          $C->{'mary'} => \@B

Cycle (2):
                          $A->{'fred'} => \@B
                               $B->[3] => \%C
                       $C->{'phyllis'} => \%A

Cycle (3):
                        $A->{'george'} => \%C
                          $C->{'mary'} => \@B
                               $B->[3] => \%C

Cycle (4):
                        $A->{'george'} => \%C
                       $C->{'phyllis'} => \%A

El módulo permite comprobar la existencia de ciclos con referencias débiles mediante la función find_weakened_cycle. El programa:

lhp@nereida:~/Lperl/src/testing$ cat -n ciclosweaken.pl
 1  #!/usr/local/bin/perl -w
 2  use strict;
 3  use Scalar::Util qw(weaken);
 4  use Devel::Cycle;
 5  my $test = {
 6    fred   => [qw(a b c d e)],
 7    ethel  => [qw(1 2 3 4 5)],
 8    george => {martha => 23, agnes  => 19}
 9  };
10  $test->{george}{phyllis} = $test;
11  $test->{fred}[3]      = $test->{george};
12  $test->{george}{mary} = $test->{fred};
13  weaken($test->{george}->{phyllis});
14  find_weakened_cycle($test);
15  exit 0;

Produce la salida:

lhp@nereida:~/Lperl/src/testing$ ciclosweaken.pl
Cycle (1):
                          $A->{'fred'} => \@B
                               $B->[3] => \%C
                          $C->{'mary'} => \@B

Cycle (2):
                          $A->{'fred'} => \@B
                               $B->[3] => \%C
                   w-> $C->{'phyllis'} => \%A

Cycle (3):
                        $A->{'george'} => \%C
                          $C->{'mary'} => \@B
                               $B->[3] => \%C

Cycle (4):
                        $A->{'george'} => \%C
                   w-> $C->{'phyllis'} => \%A

El Módulo Devel::Peek

Estudie la siguiente ejecución con el depurador. El módulo Devel::Peek nos permite observar la estructura interna de las variables en el intérprete:

lhp@nereida:~/Lperl/src$ perl -MDevel::Peek -wd weakenwithcopy.pl
main::(weakenwithcopy.pl:3):    my ($ref, $copy);
main::(weakenwithcopy.pl:4):    {
  DB<1> c 10
main::(weakenwithcopy.pl:10):   print $$ref;
  DB<2> -
1:      use strict;
2:      use Scalar::Util qw(weaken);
3:      my ($ref, $copy);
4       {
5:        my $var = "hola\n";
6:        $ref = \$var;
7:        weaken($ref);
8:        $copy = $ref;
9       }
10==>   print $$ref;
  DB<2> Dump $ref
SV = RV(0x817b328) at 0x823a050
  REFCNT = 2
  FLAGS = (PADBUSY,PADMY,ROK,WEAKREF)
  RV = 0x814f7e0
  SV = PVMG(0x83dcb80) at 0x814f7e0
    REFCNT = 1
    FLAGS = (PADBUSY,PADMY,RMG,POK,pPOK)
    IV = 0
    NV = 0
    PV = 0x8238fa8 "hola\n"\0
    CUR = 5
    LEN = 8
    MAGIC = 0x83e3368
      .............................
  DB<3> Dump $copy
SV = RV(0x817b324) at 0x814f810
  REFCNT = 2
  FLAGS = (PADBUSY,PADMY,ROK)
  RV = 0x814f7e0
  SV = PVMG(0x83dcb80) at 0x814f7e0
    REFCNT = 1
    FLAGS = (PADBUSY,PADMY,RMG,POK,pPOK)
    IV = 0
    NV = 0
    PV = 0x8238fa8 "hola\n"\0
    CUR = 5
    LEN = 8
    MAGIC = 0x83e3368
      .............................

Contadores de Referencias de Listas

El siguiente ejemplo muestra como los elementos de un array tienen sus propios contadores de referencias:

DB<1> @a = 1..5
DB<2> $ra = \$a[4]
DB<3> pop @a
DB<4> p @a
1234
DB<5> p $$ra
5
la orden pop @a rompe la ligadura entre $a[4] y el valor, esto es, la ligadura-variable $a[4] es eliminada del array. Sin embargo el valor asociado no es eliminado de memoria ya que su contador de referencias todavía no es cero. La variable $ra aún esta referenciándolo.



Subsecciones
Casiano Rodríguez León
2009-10-04