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