]> git.llucax.com Git - z.facultad/75.00/informe.git/blob - source/gc.rst
49e9fddbaf83bbec6a66b7905f0cecf063fdec6d
[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`` (ver 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
826 .. fig:: fig:gc-rc-rm-1
827
828    Eliminación de la referencia ``r0`` :math:`\to` ``h1`` (parte 1).
829
830    .. subfig::
831
832       Estado inicial del grafo de conectividad.
833
834       .. digraph:: g3_1
835
836          margin = 0;
837          ratio  = fill;
838          size   = "1.9,2.6";
839          edge [ color = gray40 ];
840          node [
841             shape     = record,
842             width     = 0,
843             height    = 0,
844             style     = filled,
845             fillcolor = gray25,
846             fontcolor = white
847          ];
848
849          subgraph cluster_all {
850
851             root [
852                label     = "root\nset|<r0> r0|<r1> r1",
853                style     = filled,
854                fillcolor = gray96,
855                fontcolor = black,
856             ];
857
858             h1 [ label = "h1\n1|<l> l|<r> r" ];
859             h2 [ label = "h2\n2|<l> l|<r> r" ];
860             h3 [ label = "h3\n2|<l> l|<r> r" ];
861             h4 [ label = "h4\n1|<l> l|<r> r" ];
862             h5 [ label = "h5\n1|<l> l|<r> r" ];
863             h6 [ label = "h6\n1|<l> l|<r> r" ];
864
865             root:r0 -> h1;
866             root:r1 -> h3;
867             h1:l -> h2;
868             h1:r -> h3;
869             h2:l -> h4;
870             h2:r -> h5;
871             h3:l -> h2;
872             h3:r -> h6;
873
874          }
875
876    .. subfig::
877
878       Al ejecutarse ``update(r0, null)``, se comienza por visitar la celda
879       ``h1``.
880
881       .. digraph:: g3_2
882
883          margin = 0;
884          ratio  = fill;
885          size   = "1.9,2.6";
886          edge [ color = gray40 ];
887          node [
888             shape     = record,
889             width     = 0,
890             height    = 0,
891             style     = filled,
892             fillcolor = gray25,
893             fontcolor = white
894          ];
895
896          subgraph cluster_all {
897
898             root [
899                label     = "root\nset|<r0> r0\n*|<r1> r1",
900                style     = filled,
901                fillcolor = gray96,
902                fontcolor = black,
903             ];
904
905             h1 [ label = "h1\n1|<l> l|<r> r" ];
906             h2 [ label = "h2\n2|<l> l|<r> r" ];
907             h3 [ label = "h3\n2|<l> l|<r> r" ];
908             h4 [ label = "h4\n1|<l> l|<r> r" ];
909             h5 [ label = "h5\n1|<l> l|<r> r" ];
910             h6 [ label = "h6\n1|<l> l|<r> r" ];
911
912             root:r0 -> h1 [ style = bold, color = black ];
913             root:r1 -> h3;
914             h1:l -> h2;
915             h1:r -> h3;
916             h2:l -> h4;
917             h2:r -> h5;
918             h3:l -> h2;
919             h3:r -> h6;
920
921          }
922
923    .. subfig::
924
925       Se decrementa el contador de ``h1`` quedando en 0 (pasa a ser
926       *basura*). Se elimina primero ``h1.l`` y luego ``h1.r``.
927
928       .. digraph:: g3_3
929
930          margin = 0;
931          ratio  = fill;
932          size   = "1.9,2.6";
933          edge [ color = gray40 ];
934          node [
935             shape     = record,
936             width     = 0,
937             height    = 0,
938             style     = filled,
939             fillcolor = gray25,
940             fontcolor = white
941          ];
942
943          subgraph cluster_all {
944
945             root [
946                label     = "root\nset|<r0> r0\n*|<r1> r1",
947                style     = filled,
948                fillcolor = gray96,
949                fontcolor = black,
950             ];
951
952             subgraph marked {
953                node [ fillcolor = white, fontcolor = black ];
954                h1;
955             };
956
957             h1 [ label = "h1\n0|<l> l|<r> r" ];
958             h2 [ label = "h2\n2|<l> l|<r> r" ];
959             h3 [ label = "h3\n2|<l> l|<r> r" ];
960             h4 [ label = "h4\n1|<l> l|<r> r" ];
961             h5 [ label = "h5\n1|<l> l|<r> r" ];
962             h6 [ label = "h6\n1|<l> l|<r> r" ];
963
964             root:r0 -> h1 [ style = invis ];
965             root:r1 -> h3;
966             h1:l -> h2 [ style = bold, color = black ];
967             h1:r -> h3;
968             h2:l -> h4;
969             h2:r -> h5;
970             h3:l -> h2;
971             h3:r -> h6;
972
973          }
974
975
976 .. fig:: fig:gc-rc-rm-2
977
978    Eliminación de la referencia ``r0`` :math:`\to` ``h1`` (parte 2).
979
980    .. subfig::
981
982       Se decrementa el contador de ``h2`` pero no queda en 0 (permanece en
983       el *live set*).
984
985       .. digraph:: g3_4
986
987          margin = 0;
988          ratio  = fill;
989          size   = "1.9,2.6";
990          edge [ color = gray40 ];
991          node [
992             shape     = record,
993             width     = 0,
994             height    = 0,
995             style     = filled,
996             fillcolor = gray25,
997             fontcolor = white
998          ];
999
1000          subgraph cluster_all {
1001
1002             root [
1003                label     = "root\nset|<r0> r0\n*|<r1> r1",
1004                style     = filled,
1005                fillcolor = gray96,
1006                fontcolor = black,
1007             ];
1008
1009             subgraph marked {
1010                node [ fillcolor = white, fontcolor = black ];
1011                h1;
1012             };
1013
1014             h1 [ label = "h1\n0|<l> l|<r> r" ];
1015             h2 [ label = "h2\n1|<l> l|<r> r" ];
1016             h3 [ label = "h3\n2|<l> l|<r> r" ];
1017             h4 [ label = "h4\n1|<l> l|<r> r" ];
1018             h5 [ label = "h5\n1|<l> l|<r> r" ];
1019             h6 [ label = "h6\n1|<l> l|<r> r" ];
1020
1021             root:r0 -> h1 [ style = invis ];
1022             root:r1 -> h3;
1023             h1:l -> h2 [ style = invis ];
1024             h1:r -> h3 [ style = bold, color = black ];
1025             h2:l -> h4;
1026             h2:r -> h5;
1027             h3:l -> h2;
1028             h3:r -> h6;
1029
1030          }
1031
1032    .. subfig::
1033
1034       El contador de ``h3`` tampoco queda en 0, sigue en el *live set*.
1035
1036       .. digraph:: g3_5
1037
1038          margin = 0;
1039          ratio  = fill;
1040          size   = "1.9,2.6";
1041          edge [ color = gray40 ];
1042          node [
1043             shape     = record,
1044             width     = 0,
1045             height    = 0,
1046             style     = filled,
1047             fillcolor = gray25,
1048             fontcolor = white
1049          ];
1050
1051          subgraph cluster_all {
1052
1053             root [
1054                label     = "root\nset|<r0> r0|<r1> r1",
1055                style     = filled,
1056                fillcolor = gray96,
1057                fontcolor = black,
1058             ];
1059
1060             subgraph marked {
1061                node [ fillcolor = white, fontcolor = black ];
1062                h1;
1063             };
1064
1065             h1 [ label = "h1\n0|<l> l|<r> r" ];
1066             h2 [ label = "h2\n1|<l> l|<r> r" ];
1067             h3 [ label = "h3\n1|<l> l|<r> r" ];
1068             h4 [ label = "h4\n1|<l> l|<r> r" ];
1069             h5 [ label = "h5\n1|<l> l|<r> r" ];
1070             h6 [ label = "h6\n1|<l> l|<r> r" ];
1071
1072             root:r0 -> h1 [ style = invis ];
1073             root:r1 -> h3;
1074             h1:l -> h2 [ style = invis ];
1075             h1:r -> h3 [ style = invis ];
1076             h2:l -> h4;
1077             h2:r -> h5;
1078             h3:l -> h2;
1079             h3:r -> h6;
1080
1081          }
1082
1083
1084 Se comienza por eliminar la referencia de ``r0`` a ``h1``, que determina
1085 que ``h1`` se convirtió en *basura* (ver figura :vref:`fig:gc-rc-rm-1`). Esto
1086 conduce al decremento del contador de ``h2`` y ``h3`` que permanecen en el
1087 *live set* ya que sus contadores siguen siendo mayores a 0 (ver figura
1088 :vref:`fig:gc-rc-rm-2`).
1089
1090
1091 .. fig:: fig:gc-rc-up-1
1092
1093    Cambio en la referencia ``h2.l`` :math:`\to` ``h2`` a ``h2.l``
1094    :math:`\to` ``h5`` (parte 1).
1095
1096    .. subfig::
1097
1098       Comienza ``update(h3.l, h5)``, se incrementa el contador de ``h5``.
1099
1100       .. digraph:: g4_1
1101
1102          margin = 0;
1103          ratio  = fill;
1104          size   = "1.9,2.6";
1105          edge [ color = gray40 ];
1106          node [
1107             shape     = record,
1108             width     = 0,
1109             height    = 0,
1110             style     = filled,
1111             fillcolor = gray25,
1112             fontcolor = white
1113          ];
1114
1115          subgraph cluster_all {
1116
1117             root [
1118                label     = "root\nset|<r0> r0|<r1> r1",
1119                style     = filled,
1120                fillcolor = gray96,
1121                fontcolor = black,
1122             ];
1123
1124             subgraph marked {
1125                node [ fillcolor = white, fontcolor = black ];
1126                h1;
1127             };
1128
1129             h1 [ label = "h1\n0|<l> l|<r> r" ];
1130             h2 [ label = "h2\n1|<l> l|<r> r" ];
1131             h3 [ label = "h3\n1|<l> l\n*|<r> r" ];
1132             h4 [ label = "h4\n1|<l> l|<r> r" ];
1133             h5 [ label = "h5\n2|<l> l|<r> r" ];
1134             h6 [ label = "h6\n1|<l> l|<r> r" ];
1135
1136             root:r0 -> h1 [ style = invis ];
1137             h1:l -> h2 [ style = invis ];
1138             h1:r -> h3 [ style = invis ];
1139             root:r1 -> h3;
1140             h2:l -> h4;
1141             h2:r -> h5;
1142             h3:l -> h2;
1143             h3:l -> h5 [ style = dotted, color = black ];
1144             h3:r -> h6;
1145
1146          }
1147
1148    .. subfig::
1149
1150       Luego se procede a visitar las hijas de ``h3``, comenzando por ``h2``.
1151
1152       .. digraph:: g4_2
1153
1154          margin = 0;
1155          ratio  = fill;
1156          size   = "1.9,2.6";
1157          edge [ color = gray40 ];
1158          node [
1159             shape     = record,
1160             width     = 0,
1161             height    = 0,
1162             style     = filled,
1163             fillcolor = gray25,
1164             fontcolor = white
1165          ];
1166
1167          subgraph cluster_all {
1168
1169             root [
1170                label     = "root\nset|<r0> r0|<r1> r1",
1171                style     = filled,
1172                fillcolor = gray96,
1173                fontcolor = black,
1174             ];
1175
1176             subgraph marked {
1177                node [ fillcolor = white, fontcolor = black ];
1178                h1;
1179             };
1180
1181             h1 [ label = "h1\n0|<l> l|<r> r" ];
1182             h2 [ label = "h2\n1|<l> l|<r> r" ];
1183             h3 [ label = "h3\n1|<l> l\n*|<r> r" ];
1184             h4 [ label = "h4\n1|<l> l|<r> r" ];
1185             h5 [ label = "h5\n2|<l> l|<r> r" ];
1186             h6 [ label = "h6\n1|<l> l|<r> r" ];
1187
1188             root:r0 -> h1 [ style = invis ];
1189             h1:l -> h2 [ style = invis ];
1190             h1:r -> h3 [ style = invis ];
1191             root:r1 -> h3;
1192             h2:l -> h4;
1193             h2:r -> h5;
1194             h3:l -> h2 [ style = bold, color = black ];
1195             h3:l -> h5 [ style = dotted, color = black ];
1196             h3:r -> h6;
1197
1198          }
1199
1200    .. subfig::
1201
1202       Se decrementa el contador de ``h2`` y queda en 0 (pasa a ser
1203       *basura*). Se eliminan las referencias a las hijas.
1204
1205       .. digraph:: g4_3
1206
1207          margin = 0;
1208          ratio  = fill;
1209          size   = "1.9,2.6";
1210          edge [ color = gray40 ];
1211          node [
1212             shape     = record,
1213             width     = 0,
1214             height    = 0,
1215             style     = filled,
1216             fillcolor = gray25,
1217             fontcolor = white
1218          ];
1219
1220          subgraph cluster_all {
1221
1222             root [
1223                label     = "root\nset|<r0> r0|<r1> r1",
1224                style     = filled,
1225                fillcolor = gray96,
1226                fontcolor = black,
1227             ];
1228
1229             subgraph marked {
1230                node [ fillcolor = white, fontcolor = black ];
1231                h1; h2;
1232             };
1233
1234             h1 [ label = "h1\n0|<l> l|<r> r" ];
1235             h2 [ label = "h2\n1|<l> l|<r> r" ];
1236             h3 [ label = "h3\n1|<l> l\n*|<r> r" ];
1237             h4 [ label = "h4\n1|<l> l|<r> r" ];
1238             h5 [ label = "h5\n2|<l> l|<r> r" ];
1239             h6 [ label = "h6\n1|<l> l|<r> r" ];
1240
1241             root:r0 -> h1 [ style = invis ];
1242             h1:l -> h2 [ style = invis ];
1243             h1:r -> h3 [ style = invis ];
1244             root:r1 -> h3;
1245             h2:l -> h4 [ style = bold, color = black ];
1246             h2:r -> h5;
1247             h3:l -> h2 [ style = invis ];
1248             h3:l -> h5 [ style = dotted, color = black ];
1249             h3:r -> h6;
1250
1251          }
1252
1253
1254 .. fig:: fig:gc-rc-up-2
1255
1256    Cambio en la referencia ``h2.l`` :math:`\to` ``h2`` a ``h2.l``
1257    :math:`\to` ``h5`` (parte 2).
1258
1259    .. subfig::
1260
1261       Se decrementa el contador de ``h4`` quedando en 0, pasa a ser
1262       *basura*. Se continúa con ``h5``.
1263
1264       .. digraph:: g4_4
1265
1266          margin = 0;
1267          ratio  = fill;
1268          size   = "1.9,2.6";
1269          edge [ color = gray40 ];
1270          node [
1271             shape     = record,
1272             width     = 0,
1273             height    = 0,
1274             style     = filled,
1275             fillcolor = gray25,
1276             fontcolor = white
1277          ];
1278
1279          subgraph cluster_all {
1280
1281             root [
1282                label     = "root\nset|<r0> r0|<r1> r1",
1283                style     = filled,
1284                fillcolor = gray96,
1285                fontcolor = black,
1286             ];
1287
1288             subgraph marked {
1289                node [ fillcolor = white, fontcolor = black ];
1290                h1; h2; h4;
1291             };
1292
1293             h1 [ label = "h1\n0|<l> l|<r> r" ];
1294             h2 [ label = "h2\n1|<l> l|<r> r" ];
1295             h3 [ label = "h3\n1|<l> l\n*|<r> r" ];
1296             h4 [ label = "h4\n0|<l> l|<r> r" ];
1297             h5 [ label = "h5\n2|<l> l|<r> r" ];
1298             h6 [ label = "h6\n1|<l> l|<r> r" ];
1299
1300             root:r0 -> h1 [ style = invis ];
1301             h1:l -> h2 [ style = invis ];
1302             h1:r -> h3 [ style = invis ];
1303             root:r1 -> h3;
1304             h2:l -> h4 [ style = invis ];
1305             h2:r -> h5 [ style = bold, color = black ];
1306             h3:l -> h2 [ style = invis ];
1307             h3:l -> h5 [ style = dotted, color = black ];
1308             h3:r -> h6;
1309
1310          }
1311
1312    .. subfig::
1313
1314       Se decrementa el contador de ``h5`` pero sigue siendo mayor que 0.
1315
1316       .. digraph:: g4_5
1317
1318          margin = 0;
1319          ratio  = fill;
1320          size   = "1.9,2.6";
1321          edge [ color = gray40 ];
1322          node [
1323             shape     = record,
1324             width     = 0,
1325             height    = 0,
1326             style     = filled,
1327             fillcolor = gray25,
1328             fontcolor = white
1329          ];
1330
1331          subgraph cluster_all {
1332
1333             root [
1334                label     = "root\nset|<r0> r0|<r1> r1",
1335                style     = filled,
1336                fillcolor = gray96,
1337                fontcolor = black,
1338             ];
1339
1340             subgraph marked {
1341                node [ fillcolor = white, fontcolor = black ];
1342                h1; h2; h4;
1343             };
1344
1345             h1 [ label = "h1\n0|<l> l|<r> r" ];
1346             h2 [ label = "h2\n1|<l> l|<r> r" ];
1347             h3 [ label = "h3\n1|<l> l\n*|<r> r" ];
1348             h4 [ label = "h4\n0|<l> l|<r> r" ];
1349             h5 [ label = "h5\n1|<l> l|<r> r" ];
1350             h6 [ label = "h6\n1|<l> l|<r> r" ];
1351
1352             root:r0 -> h1 [ style = invis ];
1353             h1:l -> h2 [ style = invis ];
1354             h1:r -> h3 [ style = invis ];
1355             root:r1 -> h3;
1356             h2:l -> h4 [ style = invis ];
1357             h2:r -> h5 [ style = invis ];
1358             h3:l -> h5 [ style = bold, color = black ];
1359             h3:l -> h2 [ style = invis ];
1360             h3:r -> h6;
1361
1362          }
1363
1364    .. subfig::
1365
1366       Se termina por actualizar la referencia de ``h3.l`` para que apunte
1367       a ``h5``.
1368
1369       .. digraph:: g4_6
1370
1371          margin = 0;
1372          ratio  = fill;
1373          size   = "1.9,2.6";
1374          edge [ color = gray40 ];
1375          node [
1376             shape     = record,
1377             width     = 0,
1378             height    = 0,
1379             style     = filled,
1380             fillcolor = gray25,
1381             fontcolor = white
1382          ];
1383
1384          subgraph cluster_all {
1385
1386             root [
1387                label     = "root\nset|<r0> r0|<r1> r1",
1388                style     = filled,
1389                fillcolor = gray96,
1390                fontcolor = black,
1391             ];
1392
1393             subgraph marked {
1394                node [ fillcolor = white, fontcolor = black ];
1395                h1; h2; h4;
1396             };
1397
1398             h1 [ label = "h1\n0|<l> l|<r> r" ];
1399             h1 [ label = "h1\n0|<l> l|<r> r" ];
1400             h2 [ label = "h2\n0|<l> l|<r> r" ];
1401             h3 [ label = "h3\n1|<l> l|<r> r" ];
1402             h4 [ label = "h4\n0|<l> l|<r> r" ];
1403             h5 [ label = "h5\n1|<l> l|<r> r" ];
1404             h6 [ label = "h6\n1|<l> l|<r> r" ];
1405
1406             root:r0 -> h1 [ style = invis ];
1407             h1:l -> h2 [ style = invis ];
1408             h1:r -> h3 [ style = invis ];
1409             root:r1 -> h3;
1410             h2:l -> h4 [ style = invis ];
1411             h2:r -> h5 [ style = invis ];
1412             h3:l -> h5;
1413             h3:l -> h2 [ style = invis ];
1414             h3:r -> h6;
1415
1416          }
1417
1418
1419 Luego se cambia una referencia (en vez de eliminarse) realizándose la
1420 operación ``update(h3.l, h5)``. Para esto primero se incrementa el contador
1421 de referencias de ``h5`` para evitar confundirlo accidentalmente con
1422 *basura* si se elimina alguna celda que apuntaba a ésta. Luego se procede
1423 a decrementar el contador de ``h2`` que queda en 0, transformándose en
1424 *basura* (ver figura :vref:`fig:gc-rc-up-1`). Lo mismo pasa cuando se
1425 desciende a ``h4``, pero al descender a ``h5`` y decrementar el contador,
1426 éste sigue siendo mayor que 0 (pues ``h3`` va a apuntar a ``h5``) así que
1427 permanece en el *live set*. Finalmente se termina de actualizar la
1428 referencia ``h3.l`` para que apunte a ``h5`` (ver figura
1429 :vref:`fig:gc-rc-up-2`).
1430
1431
1432 Marcado y barrido
1433 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1434
1435 TODO
1436
1437
1438
1439 Copia/Semi-espacio
1440 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1441
1442 TODO
1443
1444
1445
1446 .. _ref_gc_art:
1447
1448 Estado del arte
1449 ----------------------------------------------------------------------------
1450
1451 .. explicar la cantidad de cosas que hay (que son muchas) y dar algunos
1452    ejemplos.
1453
1454 TODO
1455
1456 Clasificación
1457 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1458
1459 La cantidad de clasificaciones que pueden hacerse respecto a algoritmos de
1460 recolección de basura son muy grandes. Muchas de estas clasificaciones se
1461 superponen entre sí, habiendo algoritmos que aplican muchas técnicas a la
1462 vez o recolectores híbridos que aplican una técnica para algún tipo de
1463 objeto y otra para otros.
1464
1465 A continuación se enumeran las clasificaciones más importantes que se
1466 pudieron observar en la investigación sobre el `estado del arte`_.
1467
1468 .. _ref_gc_direct:
1469
1470 Directa / indirecta
1471 ^^^^^^^^^^^^^^^^^^^
1472
1473 Generalmente se llama recolección **directa** a aquella en la cual el
1474 compilador o lenguaje instrumenta al *mutator* de forma tal que la
1475 información de conectividad se mantenga activamente cada vez que hay un
1476 cambio en él. Normalmente se utiliza un contador de referencia en cada
1477 celda para este propósito, permitiendo almacenar en todo momento la
1478 cantidad de nodos que apuntan a ésta (dejando de lado el :ref:`problema de
1479 los ciclos <ref_gc_rc_cycles>`). Esto permite reclamar una celda
1480 instantáneamente cuando el *mutator* deja de hacer referencia a ella. Este
1481 tipo de recolectores son, inherentemente :ref:`incrementales <ref_gc_inc>`.
1482
1483 Por el contrario, los recolectores **indirectos** normalmente no
1484 interfieren con el *mutator* en cada actualización del grafo de
1485 conectividad (exceptuando algunos :ref:`recolectores incrementales
1486 <ref_gc_inc>` que a veces necesitan instrumentar el *mutator* pero no para
1487 mantener el estado del grafo de conectividad completo). La recolección se
1488 dispara usualmente cuando el *mutator* requiere alocar memoria pero no hay
1489 más memoria libre conocida disponible y el recolector se encarga de generar
1490 la información de conectividad desde cero para determinar qué celdas son
1491 *basura*.
1492
1493 Estas son las dos grandes familias de recolectores, también conocidos como
1494 `conteo de referencias`_ (directa) y *traicing garbage collection*
1495 (indirecta).
1496
1497
1498 .. _ref_gc_inc:
1499
1500 Incremental
1501 ^^^^^^^^^^^
1502
1503 Recolección incremental es aquella que se realiza de forma intercalada con
1504 el *mutator*. En general el propósito es disminuir el tiempo de las pausas
1505 causadas por el recolector (aunque generalmente el resultado es un mayor
1506 costo en tiempo total de recolección).
1507
1508 De los `algoritmos clásicos`_ el único que es incremental en su forma más
1509 básica es el `conteo de referencias`_. Otros recolectores pueden hacerse
1510 incrementales de diversas maneras, pero en general consta de hacer parte
1511 del trabajo de escanear el grafo de conectividad cada vez que el *mutator*
1512 aloca memoria. En general para hacer esto es también necesario instrumentar
1513 al *mutator* de forma tal que informe al recolector cada vez que cambia el
1514 grafo de conectividad, para que éste pueda marcar al sub-grafo afectado por
1515 el cambio como *desactualizado* y así re-escanearlo nuevamente en la
1516 próxima iteración.
1517
1518 En general la eficiencia de los recolectores incrementales disminuye
1519 considerablemente cuando el *mutator* actualiza muy seguido el grafo de
1520 conectividad, porque debe re-escanear sub-grafos que ya había escaneado una
1521 y otra vez. A esto se debe también que en general el tiempo de
1522 procesamiento total de un recolector incremental sea mayor que uno no
1523 incremental, aunque el tiempo de pausa de una recolección sea menor.
1524
1525
1526 .. _ref_gc_concurrent:
1527
1528 Concurrente / *stop-the-world*
1529 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1530
1531 Los recolectores concurrentes son aquellos que pueden correr en paralelo
1532 con el *mutator*. Por el contrario, aquellos que pausan el *mutator* para
1533 realizar la recolección son usualmente denominados *stop-the-world* (*parar
1534 el mundo*), haciendo referencia a que pausan todos los hilos del *mutator*
1535 para poder escanear el grafo de conectividad de forma consistente.
1536
1537 Para lograr que un recolector sea concurrente generalmente el mecanismo es
1538 similar al necesario para hacer un :ref:`recolector incremental
1539 <ref_gc_inc>`: hay que instrumentar al *mutator* para que informe al
1540 recolector cuando se realiza algún cambio en el grafo de conectividad, de
1541 forma tal que pueda volver a escanear el sub-grafo afectado por el cambio.
1542
1543 Esto también trae como consecuencia el incremento en el tiempo total que
1544 consume el recolector, debido a la necesidad de re-escanear sub-grafos que
1545 han sido modificados.
1546
1547
1548 Cloning
1549
1550
1551 1. Instrumentar el *mutator* de forma tal de que informe al recolector cada
1552    vez que cambia el grafo de conectividad, de manera que pueda re-escanear
1553    el sub-grafo que afectado por el cambio.
1554
1555 2. Tomar una foto de la memoria y escanear esa foto estática en vez de la
1556    memoria original, de forma que cualquier cambio en del *mutator* no
1557    afecte la vista de la memoria del recolector.
1558
1559 Ambos métodos tienen ventajas y desventajas. El primero no necesita hacer
1560 una copia costosa de la memoria, pero existe la posibilidad de que el
1561 recolector nunca pueda escanear el grafo de conectividad completo, si el
1562 *mutator* modifica el grafo más rápido de lo que el recolector lo escanea.
1563 Si bien hay formas de evitar esto, es usual que el trabajo de recolección
1564 se vea considerablemente incrementado por la cantidad de veces que hay que
1565 re-escanear el grafo. El segundo método puede ser costoso por las copias
1566 necesarias (aunque esto puede ser mitigado empleando alguna forma de *COW*
1567 [#gccow]_) y existe la posibilidad de que no se recuperen celdas que el
1568 *mutator*
1569
1570
1571 .. [#gccow] *COW* (del inglés *Copy On Write* o *copiar al escribir*) es
1572    una técnica muy utilizada para disminuir las copias innecesarias,
1573    evitando hacer la copia hasta que haya una modificación. Mientras no
1574    hayan modificaciones no es necesario realizar una copia porque todos los
1575    interesados verán la misma información. Esta técnica es utilizada por el
1576    *kernel* de Linux_ por ejemplo, para que varias instancias de un mismo
1577    proceso puedan compartir la memoria.
1578
1579
1580 Boehm, H., and M. Weiser, "Garbage Collection in an Uncooperative
1581 Environment", Software Practice & Experience, September 1988, pp. 807-820.
1582 http://www.hpl.hp.com/personal/Hans_Boehm/spe_gc_paper/preprint.pdf
1583 (http://www.hpl.hp.com/personal/Hans_Boehm/spe_gc_paper/)
1584
1585 Boehm, H., "Space Efficient Conservative Garbage Collection", Proceedings
1586 of the ACM SIGPLAN '93 Conference on Programming Language Design and
1587 Implementation, SIGPLAN Notices 28, 6 (June 1993), pp. 197-206.
1588 http://www.hpl.hp.com/personal/Hans_Boehm/gc/papers/pldi93.ps.Z
1589
1590 Boehm, H., A. Demers, and S. Shenker, "Mostly Parallel Garbage Collection",
1591 Proceedings of the ACM SIGPLAN '91 Conference on Programming Language
1592 Design and Implementation, SIGPLAN Notices 26, 6 (June 1991), pp. 157-164.
1593 http://www.hpl.hp.com/personal/Hans_Boehm/gc/papers/pldi91.ps.Z
1594
1595 Implementación de Bohem GC
1596 http://www.hpl.hp.com/personal/Hans_Boehm/gc/gcdescr.html
1597
1598 Interfaz de Bohem GC
1599 http://www.hpl.hp.com/personal/Hans_Boehm/gc/gcinterface.html
1600
1601
1602 .. include:: links.rst
1603
1604 .. vim: set ts=3 sts=3 sw=3 et tw=78 :