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