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