]> git.llucax.com Git - z.facultad/75.00/informe.git/blob - source/gc.rst
Agregar más tipos de referencia a la extensión vref
[z.facultad/75.00/informe.git] / source / gc.rst
1
2 .. Introducción a la importancia de la recolección de basura y sus
3    principales técnicas, con sus ventajas y desventajas. También se da
4    un breve recorrido sobre el estado del arte.
5    ESTADO: SIN EMPEZAR
6
7 .. _ref_gc:
8
9 Recolección de basura
10 ============================================================================
11
12
13 .. _ref_gc_intro:
14
15 Introducción
16 ----------------------------------------------------------------------------
17
18 *Recolección de basura* se refiere a la recuperación automática de memoria
19 del *heap* [#gcheap]_ una vez que el programa ha dejado de hacer referencia
20 a ella (y por lo tanto, ha dejado de utilizarla).
21
22 .. [#gcheap] *Heap* es un área de memoria que se caracteriza por ser
23    dinámica (a diferencia del área de memoria estática que está disponible
24    durante toda la ejecución de un programa). Un programa puede reservar
25    memoria en tiempo de ejecución según sea necesario y liberarla cuando ya
26    no la necesita. A diferencia del *stack*, la duración de la *reserva* no
27    está atada a un bloque de código.
28
29 A medida que el tiempo pasa, cada vez los programas son más complejos y es
30 más compleja la administración de memoria. Uno de los aspectos más
31 importantes de un recolector de basura es lograr un mayor nivel de
32 abstracción y modularidad, dos conceptos claves en la ingeniería de
33 software [JOLI96]_. En particular, al diseñar o programar bibliotecas, de
34 no haber un recolector de basura, **la administración de memoria pasa a ser
35 parte de la interfaz**, lo que produce que los módulos tengan un mayor
36 grado de acoplamiento.
37
38 Además hay una incontable cantidad de problemas asociados al manejo
39 explícito de memoria que simplemente dejan de existir al utilizar un
40 recolector de basura. Por ejemplo, los errores en el manejo de memoria
41 (como *buffer overflows* [#gcbuff]_ o *dangling pointers* [#gcdang]_) son
42 la causa más frecuente de problemas de seguridad [BEZO06]_.
43
44 .. [#gcbuff] Un *buffer overflow* (*desbordamiento de memoria* en
45    castellano) se produce cuando se copia un dato a un área de memoria que
46    no es lo suficientemente grande para contenerlo. Esto puede producir que
47    el programa sea abortado por una violación de segmento, o peor,
48    sobreescribir un área de memoria válida, en cuyo caso los resultados son
49    impredecibles.
50
51 .. [#gcdang] Un *dangling pointer* (*puntero colgante* en castellano) es un
52    puntero que apunta a un área de memoria inválida. Ya sea porque el
53    elemento apuntado no es el mismo tipo o porque la memoria ya ha sido
54    liberada. Al ser desreferenciado, los resultados son impredecibles, el
55    programa podría abortarse por una violación de segmento o podrían pasar
56    peores cosas si el área de memoria fue realocada para almacenar otro
57    objeto.
58
59 La recolección de basura nació junto a Lisp_ a finales de 1950 y en los
60 siguientes años estuvo asociada principalmente a lenguajes funcionales,
61 pero en la actualidad está presente en prácticamente todos los lenguajes de
62 programación, de alto o bajo nivel, aunque sea de forma opcional. En los
63 últimos 10 años tuvo un gran avance, por la adopción en lenguajes de
64 desarrollo rápido utilizados mucho en el sector empresarial, en especial
65 Java_, que fue una plataforma de facto para la investigación y desarrollo
66 de recolectores de basura (aunque no se limitaron a este lenguaje las
67 investigaciones).
68
69 En las primeras implementaciones de recolectores de basura la penalización
70 en la eficiencia del programa se volvía prohibitiva para muchas
71 aplicaciones. Es por esto que hubo bastante resistencia a la utilización de
72 recolectores de basura, pero el avance en la investigación fue haciendo que
73 cada vez sea una alternativa más viable al manejo manual de memoria,
74 incluso para apliaciones con altos requerimientos de eficiencia. En la
75 actualidad un programa que utiliza un recolector moderno puede ser
76 comparable en eficiencia con uno que utiliza un esquema manual. En
77 particular, si el programa fue diseñado con el recolector de basura en
78 mente en ciertas circunstancias puede ser incluso más eficiente que uno que
79 hace manejo explícito de la memoria. Muchos recolectores mejoran la
80 localidad de referencia, haciendo que el programa tenga un mejor
81 comportamiento con el caché y la memoria virtual.
82
83 El recolector de basura debe tener un comportamiento correcto y predecible
84 para que sea útil, si el programador no puede confiar en el recolector
85 de basura, éste se vuelve más un problema que una solución, porque
86 introduce nuevos puntos de falla en los programas, y lo que es peor,
87 puntos de falla no controlados por el programador, volviendo mucho más
88 difícil la búsqueda de errores.
89
90
91
92
93 Conceptos básicos
94 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
95
96 Los programas pueden hacer uso principalmente de 4 áreas de memoria:
97
98 Registros:
99    Se trata de la memoria más básica de una computadora. Es el área de
100    memoria en la que puede operar realmente el procesador, es extremadamente
101    escasa y generalmente su uso es administrado por el lenguaje de
102    programación (o compilador más específicamente). Excepto en situaciones
103    muy particulares, realizando tareas de muy bajo nivel, un programador
104    nunca manipula los registros explícitamente.
105
106 Área de memoria estática:
107    Es la forma de memoria más simple que un programador utiliza
108    explícitamente. En general las variables globales se almacenan en este
109    área, que es parte inherente del programa y está disponible durante toda
110    su ejecución, por lo tanto nunca cambia su capacidad en tiempo de
111    ejecución. Es la forma más básica de administrar memoria, pero tiene una
112    limitación fundamental: **el tamaño de la memoria tiene que ser conocido
113    en tiempo de compilación**. Los primeros lenguajes de programación solo
114    contaban con este tipo de memoria (además de los registros del
115    procesador).
116
117 *Stack* (pila):
118    Los primeros lenguajes de programación que hicieron uso de una pila
119    aparecieron en el año 1958 (Algol-58 y Atlas Autocode) y fueron los
120    primeros en introducir estructura de bloques, almacenando las
121    variables locales a estos bloques utilizando una pila [JOLI96]_.
122    Esto permite utilizar recursividad y tener un esquema simple de memoria
123    dinámica. Sin embargo este esquema es muy limitado porque el orden de
124    reserva y liberación de memoria tiene que estar bien establecido. Una
125    celda [#gccelda]_ alocada antes que otra nunca puede ser liberada antes
126    que aquella.
127
128    .. [#gccelda] En general en la literatura se nombra a una porción de
129       memoria alocada individualmente *celda*, *nodo* u *objeto*
130       indistintamente. En este trabajo se utilizará la misma nomenclatura
131       (haciendo mención explícita cuando alguno de estos términos se
132       refiera a otra cosa, como al nodo de una lista o a un objeto en el
133       sentido de programación orientada a objetos).
134
135 *Heap*:
136    A diferencia del *stack*, el *heap* provee un área de memoria que puede
137    ser obtenida dinámicamente pero sin limitaciones de orden. Es el tipo de
138    memoria más flexible y por lo tanto el más complejo de administrar; razón
139    por la cual existen los recolectores de basura.
140
141 La recolección de basura impone algunas restricciones sobre la manera de
142 utilizar el *heap*. Debido a que un recolector de basura debe ser capaz de
143 determinar el grafo de conectividad de la memoria en uso, es necesario que
144 el programa siempre tenga alguna referencia a las celdas activas en los
145 registros, memoria estática o *stack* (normalmente denominado *root set*).
146
147 Esto implica que una celda sea considerada basura si y sólo si no puede ser
148 alcanzada a través del grafo de conectividad que se comienza a recorrer
149 desde el *root set*. Por lo tanto, una celda está *viva* si y sólo si su
150 dirección de memoria está almacenada en una celda *raíz* (parte del *root
151 set*) o si está almacenada en otra celda *viva* del *heap*.
152
153 Expresado más formalmente, dada la relación :math:`M \to N`, donde
154 :math:`M` es una celda del *heap* o parte del *root set* y :math:`N` es una
155 celda del *heap*, definida como:
156
157 .. math::
158
159    M \to N \Longleftrightarrow M \text{ almacena un puntero a } N
160
161 El conjunto de celdas vivas (o *live set*) queda determinado por:
162
163 .. math::
164
165    vivas = \left\lbrace N \in Celdas \big/
166       ( \exists r \in Raices / r \to N ) \vee (\exists M \in vivas / M \to N )
167    \right\rbrace
168
169 Cabe aclarar que esta es una definición conceptual, asumiendo que el
170 programa siempre limpia una dirección de memoria almacenada en el *root
171 set* o una celda del *heap* cuando la celda a la que apunta no va a ser
172 utilizada nuevamente. Esto no es siempre cierto y los falsos positivos que
173 esto produce se conoce como un tipo de pérdida de memoria (que es posible
174 incluso al utilizar un recolector de basura) llamada pérdida de memoria
175 *lógica*. Esto puede no ser evitable (incluso cuando el programador no
176 cometa errores) en lenguajes de programación que requieran un recolector de
177 basura conservativo.
178
179 Por último, siendo que el recolector de basura es parte del programa de
180 forma indirecta, es común ver en la literatura que se direfencia entre
181 2 partes del programa, el recolector de basura y el programa en sí. Dado
182 que para el recolector de basura, lo único que interesa conocer del
183 programa en sí son los cambios al grafo de conectividad de las celdas,
184 normalmente se lo llama *mutator* (mutador).
185
186
187
188 .. _ref_gc_intro_mark:
189
190 Recorrido del grafo de conectividad
191 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
192
193 El problema de encontrar las celdas *vivas* de un programa se reduce a recorrer
194 un grafo dirigido. El grafo se define como:
195
196 .. math::
197
198    G = (V,A)
199
200 Donde :math:`V` es el conjunto de vértices, dado por las celdas de memoria
201 y :math:`A` es un conjunto de pares ordenados (aristas), dado por la
202 relación :math:`M \rightarrow N` (es decir, los punteros).
203
204 El grafo comienza a recorrerse desde el *root set* y todos los vértices que
205 fueron visitados componen el *live set*; el resto de los vértices son
206 *basura*.
207
208 Más formalmente, Definimos:
209
210 *Camino*:
211    secuencia de vértices tal que cada uno de los vértices tiene una arista
212    al próximo vértice en la secuencia. Todo camino finito tiene un *vértice
213    inicial* y un *vértice final* (llamados en conjunto *vértices
214    terminales*). Cualquier vértice no terminal es denominado *vértice
215    interior*.
216
217    .. math::
218
219       \underset{v_1 \rightarrow v_N}{C} = \left\lbrace
220          v_1, \dotsc, v_N \in V \big/ \underset{i \in [1,N-1]}{\forall v_i}
221             \exists (v_i \to v_{i+1}) \in A
222       \right\rbrace
223
224 *Conexión*:
225    decimos que :math:`M` está *conectado* a :math:`N` si y sólo si existe un
226    camino de :math:`M` a :math:`N`.
227
228    .. math::
229
230       M \mapsto N \Longleftrightarrow \exists \underset{M \to N}{C} \in G
231
232 *Live set*:
233    el conjunto de celdas *vivas* está dado por todos los vértices
234    (:math:`v`) del grafo para los cuales existe una raíz en el *root set*
235    que esté conectada a él.
236
237    .. math::
238
239       Live \thickspace set = \left\lbrace v \in V \big/
240          \left( \exists r \in Root \thickspace set \big/ r \mapsto v \right)
241       \right\rbrace
242
243 *Basura*:
244    la basura, o celdas *muertas*, quedan determinadas entonces por todas las
245    celdas del *heap* que no son parte del *live set*.
246
247    .. math::
248
249       Basura = V - Live \thickspace set
250
251 Esto es, efectivamente, una partición del *heap* (ver figura
252 :vref:`fig:gc-heap-parts`).
253
254
255 .. fig:: fig:gc-heap-parts
256
257    Distintas partes de la memoria, incluyendo relación entre *basura*,
258    *live set*, *heap* y *root set*.
259
260    .. digraph:: g1
261
262       margin  = 0;
263       ratio   = fill;
264       size    = "4.6,3.6";
265       node [ shape = record, width = 0, height = 0 ];
266
267       subgraph cluster_heap {
268          label = "Heap";
269          style     = dashed;
270          color     = gray40;
271          fontcolor = gray40;
272
273          subgraph cluster_live {
274             label     = "Live set";
275             style     = dashed;
276             color     = gray40;
277             fontcolor = gray40;
278             node [
279                style     = filled,
280                fillcolor = gray25,
281                fontcolor = white,
282             ];
283             h1; h2; h4; h5; h6;
284          }
285
286          subgraph cluster_garbage {
287             label     = "Basura";
288             style     = dashed;
289             color     = gray40;
290             fontcolor = gray40;
291             node [ style = filled, fillcolor = white ];
292             h7; h3;
293          }
294       }
295
296       subgraph cluster_root {
297          label     = "Root set";
298          style     = dashed;
299          color     = gray40;
300          fontcolor = gray40;
301          node [ style = filled, fillcolor = gray96 ];
302          r0; r1; r2;
303       }
304
305       r0 -> h1 -> h2 -> h5;
306       r1 -> h5 -> h6 -> h1;
307       r2 -> h4;
308       h5 -> h4;
309       h7 -> h1;
310       h7 -> h3;
311
312
313 Al proceso de visitar los vértices *conectados* desde el *root set* se lo
314 denomina *marcado*, *fase de marcado* o *mark phase* en inglés, debido
315 a que es necesario marcar los vértices para evitar visitar 2 veces el mismo
316 nodo en casos de que el grafo contenga ciclos [#gccycle]_. De forma similar
317 a la búsqueda, que puede realizarse *primero a lo ancho* (*breadth-first*)
318 o *primero a lo alto* (*depth-first*) del grafo, el marcado de un grafo
319 también puede realizarse de ambas maneras. Cada una podrá o no tener
320 efectos en la eficiencia, en particular dependiendo de la aplicación puede
321 convenir uno u otro método para lograr una mejor localidad de referencia
322 y de esta manera tener un mejor comportamiento de la memoria virtual o del
323 *caché*.
324
325 .. [#gccycle] Un ciclo es un camino donde el *vértice inicial* es el mismo
326    que el *vértice final*. Por lo tanto, los *vértices terminales* son
327    completamente arbitrarios, ya que cualquier *vértice interior* puede ser
328    un *vértice terminal*.
329
330 Un algoritmo simple (recursivo) de marcado *primero a lo alto*  puede ser
331 el siguiente (asumiendo que partimos con todos los vértices sin marcar)
332 [#gcpseudo]_::
333
334    function mark(v) is
335       if not v.marked
336          v.marked = true
337          for (src, dst) in v.edges
338             mark(dst)
339
340    function mark_phase() is
341       foreach r in root_set
342          mark(r)
343
344 .. [#gcpseudo] Para presentar los algoritmos se utiliza una forma simple de
345    pseudo-código. El pseudo-código se escribe en inglés para que pueda ser
346    más fácilmente contrastado con la literatura, que está en inglés. Para
347    diferenciar posiciones de memoria y punteros de las celdas en sí, se usa
348    la misma sintaxis que C, ``r*`` denota una referencia o puntero y ``*r``
349    denota "objeto al que apunta ``r``\ ". Se sobreentiende que ``r = o``
350    siempre toma la dirección de memoria de ``o``.
351
352 Una vez concluido el marcado, sabemos que todos los vértices con la marca
353 son parte del *live set* y que todos los vértices no marcados son *basura*.
354 Esto es conocido también como **abstracción bicolor**, dado que en la
355 literatura se habla muchas veces de *colorear* las celdas. En general, una
356 celda sin marcar es de color blanco y una marcada de color negro.
357
358 Puede observarse un ejemplo del algoritmo en la figura
359 :vref:`fig:gc-mark-1`, en la cual se marca el sub-grafo apuntando por
360 ``r0``. Luego se marca el sub-grafo al que apunta ``r1`` (ver figura
361 :vref:`fig:gc-mark-2`), concluyendo con el marcado del grafo completo,
362 dejando sin marcar solamente las celdas *basura* (en blanco).
363
364
365 .. fig:: fig:gc-mark-1
366
367    Ejemplo de marcado del grafo de conectividad (parte 1).
368
369    .. subfig::
370
371       Se comienza a marcar el grafo por la raíz r0.
372
373       .. digraph:: g2_1
374
375          margin = 0;
376          ratio  = fill;
377          size   = "1.9,2.6";
378          node [ shape = record, width = 0, height = 0];
379          edge [ color = gray40 ];
380
381          subgraph cluster_all {
382
383             root [
384                label     = "root\nset|<r0> r0\n*|<r1> r1",
385                style     = filled,
386                fillcolor = gray96,
387             ];
388
389             subgraph marked {
390                node [ style = filled, fillcolor = gray25, fontcolor = white ];
391                h1;
392             };
393
394             root:r0 -> h1 [ style = bold, color = black ];
395             h1 -> h2 -> h5 -> h1;
396             root:r1 -> h6 -> h2;
397             h4 -> h3;
398             h3 -> h5;
399
400          }
401
402    .. subfig::
403
404       Luego de marcar el nodo ``h1``, se procede al ``h2``.
405
406       .. digraph:: g2_2
407
408          margin = 0;
409          ratio  = fill;
410          size   = "1.9,2.6";
411          node [ shape = record, width = 0, height = 0 ];
412          edge [ color = gray40 ];
413
414          subgraph cluster_all {
415
416             root [
417                label     = "root\nset|<r0> r0\n*|<r1> r1",
418                style     = filled,
419                fillcolor = gray96,
420             ];
421
422             subgraph marked {
423                node [ style = filled, fillcolor = gray25, fontcolor = white ];
424                h1; h2;
425             };
426
427             root:r0 -> h1 [ color = gray10 ];
428             h1 -> h2 [ style = bold, color = black ];
429             h2 -> h5 -> h1;
430             root:r1 -> h6 -> h2;
431             h4 -> h3;
432             h3 -> h5;
433
434          }
435
436    .. subfig::
437
438       Luego sigue el nodo h5.
439
440       .. digraph:: g2_3
441
442          margin = 0;
443          ratio  = fill;
444          size   = "1.9,2.6";
445          node [ shape = record, width = 0, height = 0 ];
446          edge [ color = gray40 ];
447
448          subgraph cluster_all {
449
450             root [
451                label     = "root\nset|<r0> r0\n*|<r1> r1",
452                style     = filled,
453                fillcolor = gray96,
454             ];
455
456             subgraph marked {
457                node [ style = filled, fillcolor = gray25, fontcolor = white ];
458                h1; h2; h5;
459             };
460
461             root:r0 -> h1 [ color = gray10 ];
462             h1 -> h2 [ color = gray10 ];
463             h2 -> h5 [ style = bold, color = black ];
464             h5 -> h1;
465             root:r1 -> h6 -> h2;
466             h4 -> h3;
467             h3 -> h5;
468
469          }
470
471
472 .. fig:: fig:gc-mark-2
473
474    Ejemplo de marcado del grafo de conectividad (parte 2).
475
476    .. subfig::
477
478       El nodo h5 tiene una arista al h1, pero el h1 ya fue visitado, por lo
479       tanto no se visita nuevamente.
480
481       .. digraph:: g2_4
482
483          margin = 0;
484          ratio  = fill;
485          size   = "1.9,2.6";
486          node [ shape = record, width = 0, height = 0 ];
487          edge [ color = gray40 ];
488
489          subgraph cluster_all {
490
491             root [
492                label     = "root\nset|<r0> r0\n*|<r1> r1",
493                style     = filled,
494                fillcolor = gray96,
495             ];
496
497             subgraph marked {
498                node [ style = filled, fillcolor = gray25, fontcolor = white ];
499                h1; h2; h5;
500             };
501
502             root:r0 -> h1 [ color = gray10 ];
503             h1 -> h2 [ color = gray10 ];
504             h2 -> h5 [ color = gray10 ];
505             h5 -> h1 [ style = bold, color = black ];
506             root:r1 -> h6 -> h2;
507             h4 -> h3;
508             h3 -> h5;
509
510          }
511
512    .. subfig::
513
514       Se concluye el marcado del sub-grafo al que conecta r0, se procede
515       a marcar el sub-grafo al que conecta r1, marcando al nodo h6.
516
517       .. digraph:: g2_5
518
519          margin = 0;
520          ratio  = fill;
521          size   = "1.9,2.6";
522          node [ shape = record, width = 0, height = 0 ];
523          edge [ color = gray40 ];
524
525          subgraph cluster_all {
526
527            root [
528               label     = "root\nset|<r0> r0|<r1> r1\n*",
529               style     = filled,
530               fillcolor = gray96,
531            ];
532
533            subgraph marked {
534               node [ style = filled, fillcolor = gray25, fontcolor = white ];
535               h1; h2; h5; h6;
536            };
537
538            root:r0 -> h1 [ color = gray10 ];
539            h1 -> h2 [ color = gray10 ];
540            h2 -> h5 [ color = gray10 ];
541            h5 -> h1 [ color = gray10 ];
542            root:r1 -> h6 [ style = bold, color = black ];
543            h6 -> h2;
544            h4 -> h3;
545            h3 -> h5;
546
547          }
548
549    .. subfig::
550
551       El nodo h6 tiene una arista al h2, pero éste ya fue marcado por lo
552       que no se vuelve a visitar. No hay más raíces, se finaliza el marcado
553       del grafo.
554
555       .. digraph:: g2_6
556
557          margin = 0;
558          ratio  = fill;
559          size   = "1.9,2.6";
560          node [ shape = record, width = 0, height = 0 ];
561          edge [ color = gray40 ];
562
563          subgraph cluster_all {
564
565             root [
566                label     = "root\nset|<r0> r0|<r1> r1\n*",
567                style     = filled,
568                fillcolor = gray96,
569             ];
570
571             subgraph marked {
572                node [ style = filled, fillcolor = gray25, fontcolor = white ];
573                h1; h2; h5; h6;
574             };
575
576             root:r0 -> h1 [ color = gray10 ];
577             h1 -> h2 [ color = gray10 ];
578             h2 -> h5 [ color = gray10 ];
579             h5 -> h1 [ color = gray10 ];
580             root:r1 -> h6 [ color = gray10 ];
581             h6 -> h2 [ style = bold, color = black ];
582             h4 -> h3;
583             h3 -> h5;
584
585          }
586
587
588
589 .. _ref_gc_intro_tricolor:
590
591 Abstracción tricolor
592 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
593
594 Muchos algoritmos utilizan tres colores para realizar el marcado. El tercer
595 color, gris generalmente, indica que una celda debe ser visitada. Esto
596 permite algoritmos :ref:`concurrentes <ref_gc_concurrent>`
597 e :ref:`incrementales <ref_gc_inc>`, además de otro tipo de optimizaciones.
598 Entonces, lo que plantea esta abtracción es una nueva partición del heap al
599 momento de marcar, esta vez son 3 porciones: blanca, gris y negra.
600
601 Al principio todas las celdas se pintan de blanco, excepto el *root set*
602 que se punta de gris. Luego se van obteniendo celdas del conjunto de las
603 grises y se las pinta de negro, pintando sus hijas directas de gris.
604
605 Una vez que no hay más celdas grises, tenemos la garantía de que las celdas
606 negras serán el *live set* y las celdas blancas *basura*. Esto se debe
607 a que siempre se mantiene esta invariante: **ninguna celda negra apunta
608 directamente a una celda blanca**. Las celdas blancas siempre son apuntadas
609 por celdas blancas o grises. Entonces, siempre que el conjunto de celdas
610 grises sea vacío, no habrán celdas negras conectadas a blancas, siendo las
611 celdas blancas *basura*.
612
613 El algoritmo básico para marcar con tres colores es el siguiente (asumiendo
614 que todas las celdas parten pintadas de blanco, es decir, el conjunto
615 blanco contiene todas las celdas de memoria y los conjuntos negro y gris
616 están vacíos)::
617
618    function mark_phase() is
619       foreach r in root_set
620          gray_set.add(r)
621       while not gray_set.empty()
622          v = gray_set.pop()
623          black_set.add(v)
624          for (src, dst) in v.edges
625             if v in white_set
626                white_set.remove(v)
627                gray_set.add(v)
628
629 Es simple notar que este algoritmo es naturalmente no recursivo, lo que de
630 por sí ya presenta una ventaja sobre el marcado *bicolor*.
631
632
633
634 .. _ref_gc_intro_services:
635
636 Servicios
637 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
638
639 En general todos los algoritmos de recolección de basura utilizan servicios
640 de una capa inferior [#gclowlayer]_ y proveen servicios a una capa superior
641 [#gchilayer]_.
642
643 .. [#gclowlayer] En general estos servicios están provistos directamente
644    por el sistema operativo pero también pueden estar dados por un
645    administrador de memoria de bajo nivel (o *low level allocator* en
646    inglés).
647
648 .. [#gchilayer] En general estos servicios son utilizados directamente por
649    el lenguaje de programación, pero pueden ser utilizados directamente por
650    el usuario del lenguaje si éste interatúa con el recolector, ya sea por
651    algún requerimiento particular o porque el lenguaje no tiene soporte
652    diercto de recolección de basura y el recolector está implementado como
653    una biblioteca de usuario.
654
655 A continuación se presentan las primitivas en común que utilizan todos los
656 recolectores a lo largo de este documento.
657
658 Servicios utilizados por el recolector son los siguientes:
659
660 :math:`alloc() \to cell`:
661    obtiene una nueva celda de memoria. El mecanismo por el cual se obtiene
662    la celda es indistinto para esta sección, puede ser de una lista libre,
663    puede ser de un administrador de memoria de más bajo nivel provisto por
664    el sistema operativo o la biblioteca estándar de C (``malloc()``), etc.
665    Cómo organizar la memoria es un área de investigación completa y si bien
666    está estrechamente relacionada con la recolección de basura, en este
667    trabajo no se prestará particular atención a este aspecto (salvo casos
668    donde el recolector impone una cierta organización de memoria en el *low
669    level allocator*). Por simplicidad también asumiremos (a menos que se
670    indique lo contrario) que las celdas son de tamaño fijo. Esta restricción
671    normalmente puede ser fácilmente relajada (en los recolectores que la
672    tienen).
673
674 :math:`free(cell)`:
675    libera una celda que ya no va a ser utilizada. La celda liberada debe
676    haber sido obtenida mediante ``alloc()``.
677
678 Y los servicios básicos proporcionados por el recolector son los
679 siguientes:
680
681 :math:`new() \to cell`:
682    obtiene una celda de memoria para ser utilizada por el programa.
683
684 :math:`update(ref, cell)`:
685    notifica al recolector que la referencia :math:`ref` ahora apunta
686    a :math:`cell`. Visto más formalmente, sería análogo a decir que hubo un
687    cambio en la conectividad del grafo: la arista :math:`src \to old` cambia
688    por :math:`src \to new` (donde :math:`src` es la celda que contiene la
689    referencia :math:`ref`, :math:`old` es la celda a la que apunta la
690    referencia :math:`ref` y :math:`new` es el argumento :math:`cell`). Si
691    :math:`cell` es ``null``, sería análogo a informar que se elimina la
692    arista :math:`src \to old`.
693
694 :math:`del(cell)`:
695    este servicio, según el algoritmo, puede ser utilizado para informar un
696    cambio en la conectividad del grafo, la eliminación de una arista
697    (análogo a :math:`update(ref, null)` pero sin proporcionar información
698    sobre la arista a eliminar). Esto es generalmente útil solo en
699    :ref:`conteo de referencias <ref_gc_rc>`. Para otros recolectores puede
700    significar que el usuario asegura que no hay más referencias a esta
701    celda, es decir, análogo a eliminar el conjunto de aristas
702    :math:`\big\lbrace (v, w) \in A , v \in Live \thickspace set , w \in
703    Live \thickspace set \big/ w = cell`.
704
705 :math:`collect()`:
706    indica al recolector que debe hacer un análisis del grafo de conectividad
707    en busca de *basura*. Generalmente este servicio es invocado por el
708    propio recolector cuando no hay más celdas reciclables.
709
710 No todos los servicios son implementados por todos los recolectores, pero
711 son lo suficientemente comunes como para describirlos de forma general en
712 esta sección. Algunos son principalmente ideados para uso interno del
713 recolector, aunque en ciertas circunstancias pueden ser utilizados por el
714 usuario también.
715
716
717
718
719 .. _ref_gc_classic:
720
721 Algoritmos clásicos
722 ----------------------------------------------------------------------------
723
724 En la literatura se encuentran normalmente referencias a 3 algoritmos
725 clásicos, que son utilizados generalmente como bloques básicos para
726 construir recolectores más complejos. Se presentan las versiones históricas
727 más simples a fin de facilitar la comprensión conceptual.
728
729
730
731 .. _ref_gc_rc:
732
733 Conteo de referencias
734 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
735
736 Se trata del algoritmo más antiguo de todos, implementado por primera vez
737 por `John McCarthy`_ para Lisp_ a finales de 1950. Se trata de un método
738 :ref:`directo <ref_gc_direct>` e :ref:`incremental <ref_gc_inc>` por
739 naturaleza, ya que distribuye la carga de la recolección de basura durante
740 toda la ejecución del programa, cada vez que el *mutator* cambia la
741 conectividad de algún nodo del grafo de conectividad.
742
743 El método consiste en tener un contador asociado a cada celda que contenga
744 la cantidad de celdas **vivas** que apuntan a ésta. Es decir, es la
745 cardinalidad del conjunto de aristas que tienen por destino a la celda.
746 Formalmente podemos definir el contador :math:`rc(v)` (de *reference
747 counter* en inglés) de la siguiente manera:
748
749 .. math::
750
751    rc(v) = \big\lvert
752       \left\lbrace
753          (v_1, v_2) \in A \big/
754             v_1 \in Live \thickspace set \cup Root \thickspace set
755                \wedge v_2 = v
756       \right\rbrace
757    \big\rvert
758
759 El *mutator* entonces debe actualizar este contador cada vez que el grafo
760 de conectividad cambia, es decir, cada vez que se agrega, modifica
761 o elimina una arista del grafo (o visto de una forma más cercana al código,
762 cada vez que se agrega, modifica o elimina un puntero).
763
764 Esta invariante es fundamental para el conteo de referencias, porque se
765 asume que si el contador es 0 entonces el *mutator* no tiene ninguna
766 referencia a la celda y por lo tanto es *basura*:
767
768 .. math::
769
770    rc(v) = 0 \Rightarrow v \in Basura
771
772 Para mantener esta invariante el *mutator*, cada vez que cambia un puntero
773 debe decrementar en 1 el contador de la celda a la que apuntaba
774 antiguamente  e incrementar en 1 el contador de la celda a la que apunta
775 luego de la modificación. Esto asegura que la invariante se mantenga
776 durante toda la ejecución del programa. Si al momento de decrementar un
777 contador éste queda en 0, la celda asociada debe liberarse de forma de
778 poder ser reciclada. Esto implica que si esta celda almacena punteros, los
779 contadores de las celdas apuntadas deben ser decrementados también, porque
780 solo deben almacenarse en el contador las aristas del *live set* para
781 mantener la invariante. De esto puede resultar que otros contadores de
782 referencia queden en 0 y más celdas sean liberadas. Por lo tanto,
783 teóricamente la complejidad de eliminar una referencia puede ser
784 :math:`O(\lvert Live \thickspace set \rvert)` en el peor caso.
785
786 Las primitivas implementadas para este tipo de recolector son las
787 siguientes (acompañadas de una implementación básica)::
788
789    function new() is
790       cell = alloc()
791       if cell is null
792          throw out_of_memory
793       cell.rc = 1
794       return cell
795
796    function del(cell) is
797       cell.rc = cell.rc - 1
798       if cell.rc is 0
799          foreach child* in cell.children
800             del(*child)
801          free(cell)
802
803    function update(ref*, cell) is
804       cell.rc = cell.rc + 1
805       del(*ref)
806       *ref = cell
807
808
809
810 .. _ref_gc_rc_cycles:
811
812 Ciclos
813 ^^^^^^
814
815 El conteo de referencias tiene, sin embargo, un problema fundamental:
816 **falla con estructuras cíclicas**. Esto significa que siempre que haya un
817 ciclo en el grafo de conectividad, hay una pérdida de memoria potencial en
818 el programa. Un ciclo es un camino :math:`\underset{v \to v}{C}`, es decir,
819 el *vértice inicial* es el mismo que el *vértice final*.
820
821 Cuando esto sucede, las celdas que participan del ciclo tienen siempre su
822 contador mayor que 0, sin embargo puede no haber ningún elemento del *root
823 set* que apunte a una celda dentro del ciclo, por lo tanto el ciclo es
824 *basura* (al igual que cualquier otra celda que sea referenciada por el
825 ciclo pero que no tenga otras referencias externas) y sin embargo los
826 contadores no son 0. Los ciclos, por lo tanto, *rompen* la invariante del
827 conteo de referencia.
828
829 Hay formas de solucionar esto, pero siempre recaen en un esquema que va por
830 fuera del conteo de referencias puro. En general los métodos para
831 solucionar esto son variados y van desde realizar un marcado del subgrafo
832 para detectar nodos hasta tener otro recolector completo de *emergencia*,
833 pasando por tratar los ciclos como un todo contar las referencias al ciclo
834 completo en vez de a cada celda en particular.
835
836 Incluso con este problema, el conteo de referencia sin ningún tipo de
837 solución en cuanto a la detección y recolección de ciclos fue utilizado en
838 muchos lenguajes de programación sin que su necesidad sea tan evidente. Por
839 ejemplo Python_ agregó recolección de ciclos en la versión 2.0 [NAS00]_
840 (liberada en octubre de 2000) y PHP_ recién agrega detección de ciclos en
841 la versión 5.3 (todavía no liberada al momento de escribir este documento)
842 [PHP081]_.
843
844
845
846 .. _ref_gc_rc_example:
847
848 Ejemplo
849 ^^^^^^^
850
851 A continuación se presenta un ejemplo gráfico para facilitar la comprensión
852 del algoritmo. Por simplicidad se asumen celdas de tamaño fijo con dos
853 punteros, ``left`` (``l``) y ``right`` (``r``) y se muestra el contador de
854 referencias abajo del nombre de cada celda. Se parte con una pequeña
855 estructura ya construída y se muestra como opera el algoritmo al eliminar
856 o cambiar una referencia (cambios en la conectividad del grafo). En un
857 comienzo todas las celdas son accesibles desde el *root set* por lo tanto
858 son todas parte del *live set*.
859
860
861 .. fig:: fig:gc-rc-rm-1
862
863    Eliminación de la referencia ``r0`` :math:`\to` ``h1`` (parte 1).
864
865    .. subfig::
866
867       Estado inicial del grafo de conectividad.
868
869       .. digraph:: g3_1
870
871          margin = 0;
872          ratio  = fill;
873          size   = "1.9,2.6";
874          edge [ color = gray40 ];
875          node [
876             shape     = record,
877             width     = 0,
878             height    = 0,
879             style     = filled,
880             fillcolor = gray25,
881             fontcolor = white
882          ];
883
884          subgraph cluster_all {
885
886             root [
887                label     = "root\nset|<r0> r0|<r1> r1",
888                style     = filled,
889                fillcolor = gray96,
890                fontcolor = black,
891             ];
892
893             h1 [ label = "h1\n1|<l> l|<r> r" ];
894             h2 [ label = "h2\n2|<l> l|<r> r" ];
895             h3 [ label = "h3\n3|<l> l|<r> r" ];
896             h4 [ label = "h4\n1|<l> l|<r> r" ];
897             h5 [ label = "h5\n1|<l> l|<r> r" ];
898             h6 [ label = "h6\n1|<l> l|<r> r" ];
899
900             root:r0 -> h1;
901             root:r1 -> h3;
902             h1:l -> h2;
903             h1:r -> h3;
904             h2:l -> h4;
905             h2:r -> h5;
906             h3:l -> h2;
907             h3:r -> h6;
908             h6:r -> h3:r;
909
910          }
911
912    .. subfig::
913
914       Al ejecutarse ``update(r0, null)``, se comienza por visitar la celda
915       ``h1``.
916
917       .. digraph:: g3_2
918
919          margin = 0;
920          ratio  = fill;
921          size   = "1.9,2.6";
922          edge [ color = gray40 ];
923          node [
924             shape     = record,
925             width     = 0,
926             height    = 0,
927             style     = filled,
928             fillcolor = gray25,
929             fontcolor = white
930          ];
931
932          subgraph cluster_all {
933
934             root [
935                label     = "root\nset|<r0> r0\n*|<r1> r1",
936                style     = filled,
937                fillcolor = gray96,
938                fontcolor = black,
939             ];
940
941             h1 [ label = "h1\n1|<l> l|<r> r" ];
942             h2 [ label = "h2\n2|<l> l|<r> r" ];
943             h3 [ label = "h3\n3|<l> l|<r> r" ];
944             h4 [ label = "h4\n1|<l> l|<r> r" ];
945             h5 [ label = "h5\n1|<l> l|<r> r" ];
946             h6 [ label = "h6\n1|<l> l|<r> r" ];
947
948             root:r0 -> h1 [ style = bold, color = black ];
949             root:r1 -> h3;
950             h1:l -> h2;
951             h1:r -> h3;
952             h2:l -> h4;
953             h2:r -> h5;
954             h3:l -> h2;
955             h3:r -> h6;
956             h6:r -> h3:r;
957
958          }
959
960    .. subfig::
961
962       Se decrementa el contador de ``h1`` quedando en 0 (pasa a ser
963       *basura*). Se elimina primero ``h1.l`` y luego ``h1.r``.
964
965       .. digraph:: g3_3
966
967          margin = 0;
968          ratio  = fill;
969          size   = "1.9,2.6";
970          edge [ color = gray40 ];
971          node [
972             shape     = record,
973             width     = 0,
974             height    = 0,
975             style     = filled,
976             fillcolor = gray25,
977             fontcolor = white
978          ];
979
980          subgraph cluster_all {
981
982             root [
983                label     = "root\nset|<r0> r0\n*|<r1> r1",
984                style     = filled,
985                fillcolor = gray96,
986                fontcolor = black,
987             ];
988
989             subgraph marked {
990                node [ fillcolor = white, fontcolor = black ];
991                h1;
992             };
993
994             h1 [ label = "h1\n0|<l> l|<r> r" ];
995             h2 [ label = "h2\n2|<l> l|<r> r" ];
996             h3 [ label = "h3\n3|<l> l|<r> r" ];
997             h4 [ label = "h4\n1|<l> l|<r> r" ];
998             h5 [ label = "h5\n1|<l> l|<r> r" ];
999             h6 [ label = "h6\n1|<l> l|<r> r" ];
1000
1001             root:r0 -> h1 [ style = invis ];
1002             root:r1 -> h3;
1003             h1:l -> h2 [ style = bold, color = black ];
1004             h1:r -> h3;
1005             h2:l -> h4;
1006             h2:r -> h5;
1007             h3:l -> h2;
1008             h3:r -> h6;
1009             h6:r -> h3:r;
1010
1011          }
1012
1013
1014 .. fig:: fig:gc-rc-rm-2
1015    :padding: 0.5
1016
1017    Eliminación de la referencia ``r0`` :math:`\to` ``h1`` (parte 2).
1018
1019    .. subfig::
1020
1021       Se decrementa el contador de ``h2`` pero no queda en 0 (permanece en
1022       el *live set*).
1023
1024       .. digraph:: g3_4
1025
1026          margin = 0;
1027          ratio  = fill;
1028          size   = "1.9,2.6";
1029          edge [ color = gray40 ];
1030          node [
1031             shape     = record,
1032             width     = 0,
1033             height    = 0,
1034             style     = filled,
1035             fillcolor = gray25,
1036             fontcolor = white
1037          ];
1038
1039          subgraph cluster_all {
1040
1041             root [
1042                label     = "root\nset|<r0> r0\n*|<r1> r1",
1043                style     = filled,
1044                fillcolor = gray96,
1045                fontcolor = black,
1046             ];
1047
1048             subgraph marked {
1049                node [ fillcolor = white, fontcolor = black ];
1050                h1;
1051             };
1052
1053             h1 [ label = "h1\n0|<l> l|<r> r" ];
1054             h2 [ label = "h2\n1|<l> l|<r> r" ];
1055             h3 [ label = "h3\n3|<l> l|<r> r" ];
1056             h4 [ label = "h4\n1|<l> l|<r> r" ];
1057             h5 [ label = "h5\n1|<l> l|<r> r" ];
1058             h6 [ label = "h6\n1|<l> l|<r> r" ];
1059
1060             root:r0 -> h1 [ style = invis ];
1061             root:r1 -> h3;
1062             h1:l -> h2 [ style = invis ];
1063             h1:r -> h3 [ style = bold, color = black ];
1064             h2:l -> h4;
1065             h2:r -> h5;
1066             h3:l -> h2;
1067             h3:r -> h6;
1068             h6:r -> h3:r;
1069
1070          }
1071
1072    .. subfig::
1073
1074       El contador de ``h3`` tampoco queda en 0, sigue en el *live set*.
1075
1076       .. digraph:: g3_5
1077
1078          margin = 0;
1079          ratio  = fill;
1080          size   = "1.9,2.6";
1081          edge [ color = gray40 ];
1082          node [
1083             shape     = record,
1084             width     = 0,
1085             height    = 0,
1086             style     = filled,
1087             fillcolor = gray25,
1088             fontcolor = white
1089          ];
1090
1091          subgraph cluster_all {
1092
1093             root [
1094                label     = "root\nset|<r0> r0|<r1> r1",
1095                style     = filled,
1096                fillcolor = gray96,
1097                fontcolor = black,
1098             ];
1099
1100             subgraph marked {
1101                node [ fillcolor = white, fontcolor = black ];
1102                h1;
1103             };
1104
1105             h1 [ label = "h1\n0|<l> l|<r> r" ];
1106             h2 [ label = "h2\n1|<l> l|<r> r" ];
1107             h3 [ label = "h3\n2|<l> l|<r> r" ];
1108             h4 [ label = "h4\n1|<l> l|<r> r" ];
1109             h5 [ label = "h5\n1|<l> l|<r> r" ];
1110             h6 [ label = "h6\n1|<l> l|<r> r" ];
1111
1112             root:r0 -> h1 [ style = invis ];
1113             root:r1 -> h3;
1114             h1:l -> h2 [ style = invis ];
1115             h1:r -> h3 [ style = invis ];
1116             h2:l -> h4;
1117             h2:r -> h5;
1118             h3:l -> h2;
1119             h3:r -> h6;
1120             h6:r -> h3:r;
1121
1122          }
1123
1124
1125 Se comienza por eliminar la referencia de ``r0`` a ``h1``, que determina
1126 que ``h1`` se convirtió en *basura* (ver figura :vref:`fig:gc-rc-rm-1`). Esto
1127 conduce al decremento del contador de ``h2`` y ``h3`` que permanecen en el
1128 *live set* ya que sus contadores siguen siendo mayores a 0 (ver figura
1129 :vref:`fig:gc-rc-rm-2`).
1130
1131
1132 .. fig:: fig:gc-rc-up-1
1133
1134    Cambio en la referencia ``h2.l`` :math:`\to` ``h2`` a ``h2.l``
1135    :math:`\to` ``h5`` (parte 1).
1136
1137    .. subfig::
1138
1139       Comienza ``update(h3.l, h5)``, se incrementa el contador de ``h5``.
1140
1141       .. digraph:: g4_1
1142
1143          margin = 0;
1144          ratio  = fill;
1145          size   = "1.9,2.6";
1146          edge [ color = gray40 ];
1147          node [
1148             shape     = record,
1149             width     = 0,
1150             height    = 0,
1151             style     = filled,
1152             fillcolor = gray25,
1153             fontcolor = white
1154          ];
1155
1156          subgraph cluster_all {
1157
1158             root [
1159                label     = "root\nset|<r0> r0|<r1> r1",
1160                style     = filled,
1161                fillcolor = gray96,
1162                fontcolor = black,
1163             ];
1164
1165             subgraph marked {
1166                node [ fillcolor = white, fontcolor = black ];
1167                h1;
1168             };
1169
1170             h1 [ label = "h1\n0|<l> l|<r> r" ];
1171             h2 [ label = "h2\n1|<l> l|<r> r" ];
1172             h3 [ label = "h3\n2|<l> l\n*|<r> r" ];
1173             h4 [ label = "h4\n1|<l> l|<r> r" ];
1174             h5 [ label = "h5\n2|<l> l|<r> r" ];
1175             h6 [ label = "h6\n1|<l> l|<r> r" ];
1176
1177             root:r0 -> h1 [ style = invis ];
1178             h1:l -> h2 [ style = invis ];
1179             h1:r -> h3 [ style = invis ];
1180             root:r1 -> h3;
1181             h2:l -> h4;
1182             h2:r -> h5;
1183             h3:l -> h2;
1184             h3:l -> h5 [ style = dotted, color = black ];
1185             h3:r -> h6;
1186             h6:r -> h3:r;
1187
1188          }
1189
1190    .. subfig::
1191
1192       Luego se procede a visitar las hijas de ``h3``, comenzando por ``h2``.
1193
1194       .. digraph:: g4_2
1195
1196          margin = 0;
1197          ratio  = fill;
1198          size   = "1.9,2.6";
1199          edge [ color = gray40 ];
1200          node [
1201             shape     = record,
1202             width     = 0,
1203             height    = 0,
1204             style     = filled,
1205             fillcolor = gray25,
1206             fontcolor = white
1207          ];
1208
1209          subgraph cluster_all {
1210
1211             root [
1212                label     = "root\nset|<r0> r0|<r1> r1",
1213                style     = filled,
1214                fillcolor = gray96,
1215                fontcolor = black,
1216             ];
1217
1218             subgraph marked {
1219                node [ fillcolor = white, fontcolor = black ];
1220                h1;
1221             };
1222
1223             h1 [ label = "h1\n0|<l> l|<r> r" ];
1224             h2 [ label = "h2\n1|<l> l|<r> r" ];
1225             h3 [ label = "h3\n2|<l> l\n*|<r> r" ];
1226             h4 [ label = "h4\n1|<l> l|<r> r" ];
1227             h5 [ label = "h5\n2|<l> l|<r> r" ];
1228             h6 [ label = "h6\n1|<l> l|<r> r" ];
1229
1230             root:r0 -> h1 [ style = invis ];
1231             h1:l -> h2 [ style = invis ];
1232             h1:r -> h3 [ style = invis ];
1233             root:r1 -> h3;
1234             h2:l -> h4;
1235             h2:r -> h5;
1236             h3:l -> h2 [ style = bold, color = black ];
1237             h3:l -> h5 [ style = dotted, color = black ];
1238             h3:r -> h6;
1239             h6:r -> h3:r;
1240
1241          }
1242
1243    .. subfig::
1244
1245       Se decrementa el contador de ``h2`` y queda en 0 (pasa a ser
1246       *basura*). Se eliminan las referencias a las hijas.
1247
1248       .. digraph:: g4_3
1249
1250          margin = 0;
1251          ratio  = fill;
1252          size   = "1.9,2.6";
1253          edge [ color = gray40 ];
1254          node [
1255             shape     = record,
1256             width     = 0,
1257             height    = 0,
1258             style     = filled,
1259             fillcolor = gray25,
1260             fontcolor = white
1261          ];
1262
1263          subgraph cluster_all {
1264
1265             root [
1266                label     = "root\nset|<r0> r0|<r1> r1",
1267                style     = filled,
1268                fillcolor = gray96,
1269                fontcolor = black,
1270             ];
1271
1272             subgraph marked {
1273                node [ fillcolor = white, fontcolor = black ];
1274                h1; h2;
1275             };
1276
1277             h1 [ label = "h1\n0|<l> l|<r> r" ];
1278             h2 [ label = "h2\n1|<l> l|<r> r" ];
1279             h3 [ label = "h3\n2|<l> l\n*|<r> r" ];
1280             h4 [ label = "h4\n1|<l> l|<r> r" ];
1281             h5 [ label = "h5\n2|<l> l|<r> r" ];
1282             h6 [ label = "h6\n1|<l> l|<r> r" ];
1283
1284             root:r0 -> h1 [ style = invis ];
1285             h1:l -> h2 [ style = invis ];
1286             h1:r -> h3 [ style = invis ];
1287             root:r1 -> h3;
1288             h2:l -> h4 [ style = bold, color = black ];
1289             h2:r -> h5;
1290             h3:l -> h2 [ style = invis ];
1291             h3:l -> h5 [ style = dotted, color = black ];
1292             h3:r -> h6;
1293             h6:r -> h3:r;
1294
1295          }
1296
1297
1298 .. fig:: fig:gc-rc-up-2
1299
1300    Cambio en la referencia ``h2.l`` :math:`\to` ``h2`` a ``h2.l``
1301    :math:`\to` ``h5`` (parte 2).
1302
1303    .. subfig::
1304
1305       Se decrementa el contador de ``h4`` quedando en 0, pasa a ser
1306       *basura*. Se continúa con ``h5``.
1307
1308       .. digraph:: g4_4
1309
1310          margin = 0;
1311          ratio  = fill;
1312          size   = "1.9,2.6";
1313          edge [ color = gray40 ];
1314          node [
1315             shape     = record,
1316             width     = 0,
1317             height    = 0,
1318             style     = filled,
1319             fillcolor = gray25,
1320             fontcolor = white
1321          ];
1322
1323          subgraph cluster_all {
1324
1325             root [
1326                label     = "root\nset|<r0> r0|<r1> r1",
1327                style     = filled,
1328                fillcolor = gray96,
1329                fontcolor = black,
1330             ];
1331
1332             subgraph marked {
1333                node [ fillcolor = white, fontcolor = black ];
1334                h1; h2; h4;
1335             };
1336
1337             h1 [ label = "h1\n0|<l> l|<r> r" ];
1338             h2 [ label = "h2\n1|<l> l|<r> r" ];
1339             h3 [ label = "h3\n2|<l> l\n*|<r> r" ];
1340             h4 [ label = "h4\n0|<l> l|<r> r" ];
1341             h5 [ label = "h5\n2|<l> l|<r> r" ];
1342             h6 [ label = "h6\n1|<l> l|<r> r" ];
1343
1344             root:r0 -> h1 [ style = invis ];
1345             h1:l -> h2 [ style = invis ];
1346             h1:r -> h3 [ style = invis ];
1347             root:r1 -> h3;
1348             h2:l -> h4 [ style = invis ];
1349             h2:r -> h5 [ style = bold, color = black ];
1350             h3:l -> h2 [ style = invis ];
1351             h3:l -> h5 [ style = dotted, color = black ];
1352             h3:r -> h6;
1353             h6:r -> h3:r;
1354
1355          }
1356
1357    .. subfig::
1358
1359       Se decrementa el contador de ``h5`` pero sigue siendo mayor que 0.
1360
1361       .. digraph:: g4_5
1362
1363          margin = 0;
1364          ratio  = fill;
1365          size   = "1.9,2.6";
1366          edge [ color = gray40 ];
1367          node [
1368             shape     = record,
1369             width     = 0,
1370             height    = 0,
1371             style     = filled,
1372             fillcolor = gray25,
1373             fontcolor = white
1374          ];
1375
1376          subgraph cluster_all {
1377
1378             root [
1379                label     = "root\nset|<r0> r0|<r1> r1",
1380                style     = filled,
1381                fillcolor = gray96,
1382                fontcolor = black,
1383             ];
1384
1385             subgraph marked {
1386                node [ fillcolor = white, fontcolor = black ];
1387                h1; h2; h4;
1388             };
1389
1390             h1 [ label = "h1\n0|<l> l|<r> r" ];
1391             h2 [ label = "h2\n1|<l> l|<r> r" ];
1392             h3 [ label = "h3\n2|<l> l\n*|<r> r" ];
1393             h4 [ label = "h4\n0|<l> l|<r> r" ];
1394             h5 [ label = "h5\n1|<l> l|<r> r" ];
1395             h6 [ label = "h6\n1|<l> l|<r> r" ];
1396
1397             root:r0 -> h1 [ style = invis ];
1398             h1:l -> h2 [ style = invis ];
1399             h1:r -> h3 [ style = invis ];
1400             root:r1 -> h3;
1401             h2:l -> h4 [ style = invis ];
1402             h2:r -> h5 [ style = invis ];
1403             h3:l -> h5 [ style = bold, color = black ];
1404             h3:l -> h2 [ style = invis ];
1405             h3:r -> h6;
1406             h6:r -> h3:r;
1407
1408          }
1409
1410    .. subfig::
1411
1412       Se termina por actualizar la referencia de ``h3.l`` para que apunte
1413       a ``h5``.
1414
1415       .. digraph:: g4_6
1416
1417          margin = 0;
1418          ratio  = fill;
1419          size   = "1.9,2.6";
1420          edge [ color = gray40 ];
1421          node [
1422             shape     = record,
1423             width     = 0,
1424             height    = 0,
1425             style     = filled,
1426             fillcolor = gray25,
1427             fontcolor = white
1428          ];
1429
1430          subgraph cluster_all {
1431
1432             root [
1433                label     = "root\nset|<r0> r0|<r1> r1",
1434                style     = filled,
1435                fillcolor = gray96,
1436                fontcolor = black,
1437             ];
1438
1439             subgraph marked {
1440                node [ fillcolor = white, fontcolor = black ];
1441                h1; h2; h4;
1442             };
1443
1444             h1 [ label = "h1\n0|<l> l|<r> r" ];
1445             h1 [ label = "h1\n0|<l> l|<r> r" ];
1446             h2 [ label = "h2\n0|<l> l|<r> r" ];
1447             h3 [ label = "h3\n2|<l> l|<r> r" ];
1448             h4 [ label = "h4\n0|<l> l|<r> r" ];
1449             h5 [ label = "h5\n1|<l> l|<r> r" ];
1450             h6 [ label = "h6\n1|<l> l|<r> r" ];
1451
1452             root:r0 -> h1 [ style = invis ];
1453             h1:l -> h2 [ style = invis ];
1454             h1:r -> h3 [ style = invis ];
1455             root:r1 -> h3;
1456             h2:l -> h4 [ style = invis ];
1457             h2:r -> h5 [ style = invis ];
1458             h3:l -> h5;
1459             h3:l -> h2 [ style = invis ];
1460             h3:r -> h6;
1461             h6:r -> h3:r;
1462
1463          }
1464
1465
1466 Luego se cambia una referencia (en vez de eliminarse) realizándose la
1467 operación ``update(h3.l, h5)``. Para esto primero se incrementa el contador
1468 de referencias de ``h5`` para evitar confundirlo accidentalmente con
1469 *basura* si se elimina alguna celda que apuntaba a ésta. Luego se procede
1470 a decrementar el contador de ``h2`` que queda en 0, transformándose en
1471 *basura* (ver figura :vref:`fig:gc-rc-up-1`). Lo mismo pasa cuando se
1472 desciende a ``h4``, pero al descender a ``h5`` y decrementar el contador,
1473 éste sigue siendo mayor que 0 (pues ``h3`` va a apuntar a ``h5``) así que
1474 permanece en el *live set*. Finalmente se termina de actualizar la
1475 referencia ``h3.l`` para que apunte a ``h5`` (ver figura
1476 :vref:`fig:gc-rc-up-2`).
1477
1478
1479 .. fig:: fig:gc-rc-cycle
1480    :padding: 0.5
1481
1482    Eliminación de la referencia ``r1`` :math:`\to` ``h3`` (pérdida de
1483    memoria debido a un ciclo).
1484
1485    .. subfig::
1486
1487       El ejecutarse ``update(r1, null)`` se visita la celda ``h3``.
1488
1489       .. digraph:: g4_6
1490
1491          margin = 0;
1492          ratio  = fill;
1493          size   = "1.9,2.6";
1494          edge [ color = gray40 ];
1495          node [
1496             shape     = record,
1497             width     = 0,
1498             height    = 0,
1499             style     = filled,
1500             fillcolor = gray25,
1501             fontcolor = white
1502          ];
1503
1504          subgraph cluster_all {
1505
1506             root [
1507                label     = "root\nset|<r0> r0|<r1> r1\n*",
1508                style     = filled,
1509                fillcolor = gray96,
1510                fontcolor = black,
1511             ];
1512
1513             subgraph marked {
1514                node [ fillcolor = white, fontcolor = black ];
1515                h1; h2; h4;
1516             };
1517
1518             h1 [ label = "h1\n0|<l> l|<r> r" ];
1519             h1 [ label = "h1\n0|<l> l|<r> r" ];
1520             h2 [ label = "h2\n0|<l> l|<r> r" ];
1521             h3 [ label = "h3\n2|<l> l|<r> r" ];
1522             h4 [ label = "h4\n0|<l> l|<r> r" ];
1523             h5 [ label = "h5\n1|<l> l|<r> r" ];
1524             h6 [ label = "h6\n1|<l> l|<r> r" ];
1525
1526             root:r0 -> h1 [ style = invis ];
1527             h1:l -> h2 [ style = invis ];
1528             h1:r -> h3 [ style = invis ];
1529             root:r1 -> h3 [ style = bold, color = black ];
1530             h2:l -> h4 [ style = invis ];
1531             h2:r -> h5 [ style = invis ];
1532             h3:l -> h5;
1533             h3:l -> h2 [ style = invis ];
1534             h3:r -> h6;
1535             h6:r -> h3:r;
1536
1537          }
1538
1539    .. subfig::
1540
1541       Se decrementa el contador de ``h3`` pero sigue siendo mayor que 0 por
1542       el ciclo.
1543
1544       .. digraph:: g5_2
1545
1546          margin = 0;
1547          ratio  = fill;
1548          size   = "1.9,2.6";
1549          edge [ color = gray40 ];
1550          node [
1551             shape     = record,
1552             width     = 0,
1553             height    = 0,
1554             style     = filled,
1555             fillcolor = gray25,
1556             fontcolor = white
1557          ];
1558
1559          subgraph cluster_all {
1560
1561             root [
1562                label     = "root\nset|<r0> r0|<r1> r1\n*",
1563                style     = filled,
1564                fillcolor = gray96,
1565                fontcolor = black,
1566             ];
1567
1568             subgraph marked {
1569                node [ fillcolor = white, fontcolor = black ];
1570                h1; h2; h4;
1571             };
1572
1573             h1 [ label = "h1\n0|<l> l|<r> r" ];
1574             h1 [ label = "h1\n0|<l> l|<r> r" ];
1575             h2 [ label = "h2\n0|<l> l|<r> r" ];
1576             h3 [ label = "h3\n1|<l> l|<r> r" ];
1577             h4 [ label = "h4\n0|<l> l|<r> r" ];
1578             h5 [ label = "h5\n1|<l> l|<r> r" ];
1579             h6 [ label = "h6\n1|<l> l|<r> r" ];
1580
1581             root:r0 -> h1 [ style = invis ];
1582             h1:l -> h2 [ style = invis ];
1583             h1:r -> h3 [ style = invis ];
1584             root:r1 -> h3 [ style = invis ];
1585             h2:l -> h4 [ style = invis ];
1586             h2:r -> h5 [ style = invis ];
1587             h3:l -> h5;
1588             h3:l -> h2 [ style = invis ];
1589             h3:r -> h6;
1590             h6:r -> h3:r;
1591
1592          }
1593
1594
1595 Finalmente se presenta lo que sucede cuando se elimina la última referencia
1596 a un ciclo (en este caso un ciclo simple de 2 celdas: ``h3`` y ``h6``). Se
1597 elimina la única referencia externa al ciclo (``r1``), por lo que se visita
1598 la celda ``h3`` decrementando su contador de referencias, pero éste
1599 continúa siendo mayor que 0 porque la celda ``h6`` (parte del ciclo) la
1600 referencia. Por lo tanto el ciclo, y todas las celdas a las que apunta que
1601 no tienen otras referencias externas y por lo tanto deberían ser *basura*
1602 también (``h5``), no pueden ser recicladas y su memoria es perdida (ver
1603 figura :vref:`fig:gc-rc-cycle`).
1604
1605
1606
1607 .. _ref_gc_mark_sweep:
1608
1609 Marcado y barrido
1610 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1611
1612 TODO
1613
1614
1615
1616 Copia/Semi-espacio
1617 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1618
1619 TODO
1620
1621
1622
1623 .. _ref_gc_art:
1624
1625 Estado del arte
1626 ----------------------------------------------------------------------------
1627
1628 .. explicar la cantidad de cosas que hay (que son muchas) y dar algunos
1629    ejemplos.
1630
1631 TODO
1632
1633 Clasificación
1634 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1635
1636 La cantidad de clasificaciones que pueden hacerse respecto a algoritmos de
1637 recolección de basura son muy grandes. Muchas de estas clasificaciones se
1638 superponen entre sí, habiendo algoritmos que aplican muchas técnicas a la
1639 vez o recolectores híbridos que aplican una técnica para algún tipo de
1640 objeto y otra para otros.
1641
1642 A continuación se enumeran las clasificaciones más importantes que se
1643 pudieron observar en la investigación sobre el `estado del arte`_.
1644
1645 .. _ref_gc_direct:
1646
1647 Directa / indirecta
1648 ^^^^^^^^^^^^^^^^^^^
1649
1650 Generalmente se llama recolección **directa** a aquella en la cual el
1651 compilador o lenguaje instrumenta al *mutator* de forma tal que la
1652 información de conectividad se mantenga activamente cada vez que hay un
1653 cambio en él. Normalmente se utiliza un contador de referencia en cada
1654 celda para este propósito, permitiendo almacenar en todo momento la
1655 cantidad de nodos que apuntan a ésta (dejando de lado el :ref:`problema de
1656 los ciclos <ref_gc_rc_cycles>`). Esto permite reclamar una celda
1657 instantáneamente cuando el *mutator* deja de hacer referencia a ella. Este
1658 tipo de recolectores son, inherentemente :ref:`incrementales <ref_gc_inc>`.
1659
1660 Por el contrario, los recolectores **indirectos** normalmente no
1661 interfieren con el *mutator* en cada actualización del grafo de
1662 conectividad (exceptuando algunos :ref:`recolectores incrementales
1663 <ref_gc_inc>` que a veces necesitan instrumentar el *mutator* pero no para
1664 mantener el estado del grafo de conectividad completo). La recolección se
1665 dispara usualmente cuando el *mutator* requiere alocar memoria pero no hay
1666 más memoria libre conocida disponible y el recolector se encarga de generar
1667 la información de conectividad desde cero para determinar qué celdas son
1668 *basura*.
1669
1670 Estas son las dos grandes familias de recolectores, también conocidos como
1671 `conteo de referencias`_ (directa) y *traicing garbage collection*
1672 (indirecta).
1673
1674
1675 .. _ref_gc_inc:
1676
1677 Incremental
1678 ^^^^^^^^^^^
1679
1680 Recolección incremental es aquella que se realiza de forma intercalada con
1681 el *mutator*. En general el propósito es disminuir el tiempo de las pausas
1682 causadas por el recolector (aunque generalmente el resultado es un mayor
1683 costo en tiempo total de recolección).
1684
1685 De los `algoritmos clásicos`_ el único que es incremental en su forma más
1686 básica es el `conteo de referencias`_. Otros recolectores pueden hacerse
1687 incrementales de diversas maneras, pero en general consta de hacer parte
1688 del trabajo de escanear el grafo de conectividad cada vez que el *mutator*
1689 aloca memoria. En general para hacer esto es también necesario instrumentar
1690 al *mutator* de forma tal que informe al recolector cada vez que cambia el
1691 grafo de conectividad, para que éste pueda marcar al sub-grafo afectado por
1692 el cambio como *desactualizado* y así re-escanearlo nuevamente en la
1693 próxima iteración.
1694
1695 En general la eficiencia de los recolectores incrementales disminuye
1696 considerablemente cuando el *mutator* actualiza muy seguido el grafo de
1697 conectividad, porque debe re-escanear sub-grafos que ya había escaneado una
1698 y otra vez. A esto se debe también que en general el tiempo de
1699 procesamiento total de un recolector incremental sea mayor que uno no
1700 incremental, aunque el tiempo de pausa de una recolección sea menor.
1701
1702
1703 .. _ref_gc_concurrent:
1704
1705 Concurrente / *stop-the-world*
1706 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1707
1708 Los recolectores concurrentes son aquellos que pueden correr en paralelo
1709 con el *mutator*. Por el contrario, aquellos que pausan el *mutator* para
1710 realizar la recolección son usualmente denominados *stop-the-world* (*parar
1711 el mundo*), haciendo referencia a que pausan todos los hilos del *mutator*
1712 para poder escanear el grafo de conectividad de forma consistente.
1713
1714 Para lograr que un recolector sea concurrente generalmente el mecanismo es
1715 similar al necesario para hacer un :ref:`recolector incremental
1716 <ref_gc_inc>`: hay que instrumentar al *mutator* para que informe al
1717 recolector cuando se realiza algún cambio en el grafo de conectividad, de
1718 forma tal que pueda volver a escanear el sub-grafo afectado por el cambio.
1719
1720 Esto también trae como consecuencia el incremento en el tiempo total que
1721 consume el recolector, debido a la necesidad de re-escanear sub-grafos que
1722 han sido modificados.
1723
1724
1725 Cloning
1726
1727
1728 1. Instrumentar el *mutator* de forma tal de que informe al recolector cada
1729    vez que cambia el grafo de conectividad, de manera que pueda re-escanear
1730    el sub-grafo que afectado por el cambio.
1731
1732 2. Tomar una foto de la memoria y escanear esa foto estática en vez de la
1733    memoria original, de forma que cualquier cambio en del *mutator* no
1734    afecte la vista de la memoria del recolector.
1735
1736 Ambos métodos tienen ventajas y desventajas. El primero no necesita hacer
1737 una copia costosa de la memoria, pero existe la posibilidad de que el
1738 recolector nunca pueda escanear el grafo de conectividad completo, si el
1739 *mutator* modifica el grafo más rápido de lo que el recolector lo escanea.
1740 Si bien hay formas de evitar esto, es usual que el trabajo de recolección
1741 se vea considerablemente incrementado por la cantidad de veces que hay que
1742 re-escanear el grafo. El segundo método puede ser costoso por las copias
1743 necesarias (aunque esto puede ser mitigado empleando alguna forma de *COW*
1744 [#gccow]_) y existe la posibilidad de que no se recuperen celdas que el
1745 *mutator*
1746
1747
1748 .. [#gccow] *COW* (del inglés *Copy On Write* o *copiar al escribir*) es
1749    una técnica muy utilizada para disminuir las copias innecesarias,
1750    evitando hacer la copia hasta que haya una modificación. Mientras no
1751    hayan modificaciones no es necesario realizar una copia porque todos los
1752    interesados verán la misma información. Esta técnica es utilizada por el
1753    *kernel* de Linux_ por ejemplo, para que varias instancias de un mismo
1754    proceso puedan compartir la memoria.
1755
1756
1757 Boehm, H., and M. Weiser, "Garbage Collection in an Uncooperative
1758 Environment", Software Practice & Experience, September 1988, pp. 807-820.
1759 http://www.hpl.hp.com/personal/Hans_Boehm/spe_gc_paper/preprint.pdf
1760 (http://www.hpl.hp.com/personal/Hans_Boehm/spe_gc_paper/)
1761
1762 Boehm, H., "Space Efficient Conservative Garbage Collection", Proceedings
1763 of the ACM SIGPLAN '93 Conference on Programming Language Design and
1764 Implementation, SIGPLAN Notices 28, 6 (June 1993), pp. 197-206.
1765 http://www.hpl.hp.com/personal/Hans_Boehm/gc/papers/pldi93.ps.Z
1766
1767 Boehm, H., A. Demers, and S. Shenker, "Mostly Parallel Garbage Collection",
1768 Proceedings of the ACM SIGPLAN '91 Conference on Programming Language
1769 Design and Implementation, SIGPLAN Notices 26, 6 (June 1991), pp. 157-164.
1770 http://www.hpl.hp.com/personal/Hans_Boehm/gc/papers/pldi91.ps.Z
1771
1772 Implementación de Bohem GC
1773 http://www.hpl.hp.com/personal/Hans_Boehm/gc/gcdescr.html
1774
1775 Interfaz de Bohem GC
1776 http://www.hpl.hp.com/personal/Hans_Boehm/gc/gcinterface.html
1777
1778
1779 .. include:: links.rst
1780
1781 .. vim: set ts=3 sts=3 sw=3 et tw=78 :