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.
10 ============================================================================
14 ----------------------------------------------------------------------------
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).
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.
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.
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]_.
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
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
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
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.
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.
92 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
94 Los programas pueden hacer uso principalmente de 4 áreas de memoria:
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.
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
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
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).
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.
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*).
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*.
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:
157 M \to N \Longleftrightarrow M \text{ almacena un puntero a } N
159 El conjunto de celdas vivas (o *live set*) queda determinado por:
163 vivas = \left\lbrace N \in Celdas \big/
164 ( \exists r \in Raices / r \to N ) \vee (\exists M \in vivas / M \to N )
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
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).
187 Recorrido del grafo de conectividad
188 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
190 El problema de encontrar las celdas *vivas* de un programa se reduce a recorrer
191 un grafo dirigido. El grafo se define como:
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).
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
205 Más formalmente, Definimos:
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
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
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`.
227 M \mapsto N \Longleftrightarrow \exists \underset{M \to N}{C} \in G
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.
236 Live \thickspace set = \left\lbrace v \in V \big/
237 \left( \exists r \in Root \thickspace set \big/ r \mapsto v \right)
241 la basura, o celdas *muertas*, quedan determinadas entonces por todas las
242 celdas del *heap* que no son parte del *live set*.
246 Basura = V - Live \thickspace set
248 Esto es, efectivamente, una partición del *heap* (ver figura
249 :vref:`fig:gc-heap-parts`).
252 .. fig:: fig:gc-heap-parts
254 Distintas partes de la memoria, incluyendo relación entre *basura*,
255 *live set*, *heap* y *root set*.
262 node [ shape = record, width = 0, height = 0 ];
264 subgraph cluster_heap {
270 subgraph cluster_live {
283 subgraph cluster_garbage {
288 node [ style = filled, fillcolor = white ];
293 subgraph cluster_root {
298 node [ style = filled, fillcolor = gray96 ];
302 r0 -> h1 -> h2 -> h5;
303 r1 -> h5 -> h6 -> h1;
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
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*.
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)
334 for (src, dst) in v.edges
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``.
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.
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).
362 .. fig:: fig:gc-mark-1
364 Ejemplo de marcado del grafo de conectividad (parte 1).
368 Se comienza a marcar el grafo por la raíz r0.
375 node [ shape = record, width = 0, height = 0];
376 edge [ color = gray40 ];
378 subgraph cluster_all {
381 label = "root\nset|<r0> r0\n*|<r1> r1",
387 node [ style = filled, fillcolor = gray25, fontcolor = white ];
391 root:r0 -> h1 [ style = bold, color = black ];
392 h1 -> h2 -> h5 -> h1;
401 Luego de marcar el nodo ``h1``, se procede al ``h2``.
408 node [ shape = record, width = 0, height = 0 ];
409 edge [ color = gray40 ];
411 subgraph cluster_all {
414 label = "root\nset|<r0> r0\n*|<r1> r1",
420 node [ style = filled, fillcolor = gray25, fontcolor = white ];
424 root:r0 -> h1 [ color = gray10 ];
425 h1 -> h2 [ style = bold, color = black ];
435 Luego sigue el nodo h5.
442 node [ shape = record, width = 0, height = 0 ];
443 edge [ color = gray40 ];
445 subgraph cluster_all {
448 label = "root\nset|<r0> r0\n*|<r1> r1",
454 node [ style = filled, fillcolor = gray25, fontcolor = white ];
458 root:r0 -> h1 [ color = gray10 ];
459 h1 -> h2 [ color = gray10 ];
460 h2 -> h5 [ style = bold, color = black ];
469 .. fig:: fig:gc-mark-2
471 Ejemplo de marcado del grafo de conectividad (parte 2).
475 El nodo h5 tiene una arista al h1, pero el h1 ya fue visitado, por lo
476 tanto no se visita nuevamente.
483 node [ shape = record, width = 0, height = 0 ];
484 edge [ color = gray40 ];
486 subgraph cluster_all {
489 label = "root\nset|<r0> r0\n*|<r1> r1",
495 node [ style = filled, fillcolor = gray25, fontcolor = white ];
499 root:r0 -> h1 [ color = gray10 ];
500 h1 -> h2 [ color = gray10 ];
501 h2 -> h5 [ color = gray10 ];
502 h5 -> h1 [ style = bold, color = black ];
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.
519 node [ shape = record, width = 0, height = 0 ];
520 edge [ color = gray40 ];
522 subgraph cluster_all {
525 label = "root\nset|<r0> r0|<r1> r1\n*",
531 node [ style = filled, fillcolor = gray25, fontcolor = white ];
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 ];
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
557 node [ shape = record, width = 0, height = 0 ];
558 edge [ color = gray40 ];
560 subgraph cluster_all {
563 label = "root\nset|<r0> r0|<r1> r1\n*",
569 node [ style = filled, fillcolor = gray25, fontcolor = white ];
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 ];
589 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.
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.
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*.
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
618 while not gray_set.empty()
621 for (src, dst) in v.edges
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*.
634 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
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
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.
652 A continuación se presentan las primitivas en común que utilizan todos los
653 recolectores a lo largo de este documento.
655 Servicios utilizados por el recolector son los siguientes:
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
672 libera una celda que ya no va a ser utilizada. La celda liberada debe
673 haber sido obtenida mediante ``alloc()``.
675 Y los servicios básicos proporcionados por el recolector son los
678 :math:`new() \to cell`:
679 obtiene una celda de memoria para ser utilizada por el programa.
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`.
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`.
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.
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
719 ----------------------------------------------------------------------------
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.
730 Conteo de referencias
731 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.
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:
750 (v_1, v_2) \in A \big/
751 v_1 \in Live \thickspace set \cup Root \thickspace set
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).
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*:
767 rc(v) = 0 \Rightarrow v \in Basura
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.
783 Las primitivas implementadas para este tipo de recolector son las
784 siguientes (acompañadas de una implementación básica)::
794 cell.rc = cell.rc - 1
796 for child* in cell.children
801 cell.rc = cell.rc + 1
807 .. _ref_gc_rc_cycles:
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*.
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.
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.
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)
843 .. _ref_gc_rc_example:
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*.
858 .. fig:: fig:gc-rc-rm-1
860 Eliminación de la referencia ``r0`` :math:`\to` ``h1`` (parte 1).
864 Estado inicial del grafo de conectividad.
871 edge [ color = gray40 ];
881 subgraph cluster_all {
884 label = "root\nset|<r0> r0|<r1> r1",
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" ];
911 Al ejecutarse ``update(r0, null)``, se comienza por visitar la celda
919 edge [ color = gray40 ];
929 subgraph cluster_all {
932 label = "root\nset|<r0> r0\n*|<r1> r1",
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" ];
945 root:r0 -> h1 [ style = bold, color = black ];
959 Se decrementa el contador de ``h1`` quedando en 0 (pasa a ser
960 *basura*). Se elimina primero ``h1.l`` y luego ``h1.r``.
967 edge [ color = gray40 ];
977 subgraph cluster_all {
980 label = "root\nset|<r0> r0\n*|<r1> r1",
987 node [ fillcolor = white, fontcolor = black ];
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" ];
998 root:r0 -> h1 [ style = invis ];
1000 h1:l -> h2 [ style = bold, color = black ];
1011 .. fig:: fig:gc-rc-rm-2
1014 Eliminación de la referencia ``r0`` :math:`\to` ``h1`` (parte 2).
1018 Se decrementa el contador de ``h2`` pero no queda en 0 (permanece en
1026 edge [ color = gray40 ];
1036 subgraph cluster_all {
1039 label = "root\nset|<r0> r0\n*|<r1> r1",
1046 node [ fillcolor = white, fontcolor = black ];
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" ];
1057 root:r0 -> h1 [ style = invis ];
1059 h1:l -> h2 [ style = invis ];
1060 h1:r -> h3 [ style = bold, color = black ];
1071 El contador de ``h3`` tampoco queda en 0, sigue en el *live set*.
1078 edge [ color = gray40 ];
1088 subgraph cluster_all {
1091 label = "root\nset|<r0> r0|<r1> r1",
1098 node [ fillcolor = white, fontcolor = black ];
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" ];
1109 root:r0 -> h1 [ style = invis ];
1111 h1:l -> h2 [ style = invis ];
1112 h1:r -> h3 [ style = invis ];
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`).
1129 .. fig:: fig:gc-rc-up-1
1131 Cambio en la referencia ``h2.l`` :math:`\to` ``h2`` a ``h2.l``
1132 :math:`\to` ``h5`` (parte 1).
1136 Comienza ``update(h3.l, h5)``, se incrementa el contador de ``h5``.
1143 edge [ color = gray40 ];
1153 subgraph cluster_all {
1156 label = "root\nset|<r0> r0|<r1> r1",
1163 node [ fillcolor = white, fontcolor = black ];
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" ];
1174 root:r0 -> h1 [ style = invis ];
1175 h1:l -> h2 [ style = invis ];
1176 h1:r -> h3 [ style = invis ];
1181 h3:l -> h5 [ style = dotted, color = black ];
1189 Luego se procede a visitar las hijas de ``h3``, comenzando por ``h2``.
1196 edge [ color = gray40 ];
1206 subgraph cluster_all {
1209 label = "root\nset|<r0> r0|<r1> r1",
1216 node [ fillcolor = white, fontcolor = black ];
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" ];
1227 root:r0 -> h1 [ style = invis ];
1228 h1:l -> h2 [ style = invis ];
1229 h1:r -> h3 [ style = invis ];
1233 h3:l -> h2 [ style = bold, color = black ];
1234 h3:l -> h5 [ style = dotted, color = black ];
1242 Se decrementa el contador de ``h2`` y queda en 0 (pasa a ser
1243 *basura*). Se eliminan las referencias a las hijas.
1250 edge [ color = gray40 ];
1260 subgraph cluster_all {
1263 label = "root\nset|<r0> r0|<r1> r1",
1270 node [ fillcolor = white, fontcolor = black ];
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" ];
1281 root:r0 -> h1 [ style = invis ];
1282 h1:l -> h2 [ style = invis ];
1283 h1:r -> h3 [ style = invis ];
1285 h2:l -> h4 [ style = bold, color = black ];
1287 h3:l -> h2 [ style = invis ];
1288 h3:l -> h5 [ style = dotted, color = black ];
1295 .. fig:: fig:gc-rc-up-2
1297 Cambio en la referencia ``h2.l`` :math:`\to` ``h2`` a ``h2.l``
1298 :math:`\to` ``h5`` (parte 2).
1302 Se decrementa el contador de ``h4`` quedando en 0, pasa a ser
1303 *basura*. Se continúa con ``h5``.
1310 edge [ color = gray40 ];
1320 subgraph cluster_all {
1323 label = "root\nset|<r0> r0|<r1> r1",
1330 node [ fillcolor = white, fontcolor = black ];
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" ];
1341 root:r0 -> h1 [ style = invis ];
1342 h1:l -> h2 [ style = invis ];
1343 h1:r -> h3 [ style = invis ];
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 ];
1356 Se decrementa el contador de ``h5`` pero sigue siendo mayor que 0.
1363 edge [ color = gray40 ];
1373 subgraph cluster_all {
1376 label = "root\nset|<r0> r0|<r1> r1",
1383 node [ fillcolor = white, fontcolor = black ];
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" ];
1394 root:r0 -> h1 [ style = invis ];
1395 h1:l -> h2 [ style = invis ];
1396 h1:r -> h3 [ style = invis ];
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 ];
1409 Se termina por actualizar la referencia de ``h3.l`` para que apunte
1417 edge [ color = gray40 ];
1427 subgraph cluster_all {
1430 label = "root\nset|<r0> r0|<r1> r1",
1437 node [ fillcolor = white, fontcolor = black ];
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" ];
1449 root:r0 -> h1 [ style = invis ];
1450 h1:l -> h2 [ style = invis ];
1451 h1:r -> h3 [ style = invis ];
1453 h2:l -> h4 [ style = invis ];
1454 h2:r -> h5 [ style = invis ];
1456 h3:l -> h2 [ style = invis ];
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`).
1476 .. fig:: fig:gc-rc-cycle
1479 Eliminación de la referencia ``r1`` :math:`\to` ``h3`` (pérdida de
1480 memoria debido a un ciclo).
1484 El ejecutarse ``update(r1, null)`` se visita la celda ``h3``.
1491 edge [ color = gray40 ];
1501 subgraph cluster_all {
1504 label = "root\nset|<r0> r0|<r1> r1\n*",
1511 node [ fillcolor = white, fontcolor = black ];
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" ];
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 ];
1530 h3:l -> h2 [ style = invis ];
1538 Se decrementa el contador de ``h3`` pero sigue siendo mayor que 0 por
1546 edge [ color = gray40 ];
1556 subgraph cluster_all {
1559 label = "root\nset|<r0> r0|<r1> r1\n*",
1566 node [ fillcolor = white, fontcolor = black ];
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" ];
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 ];
1585 h3:l -> h2 [ style = invis ];
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`).
1606 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1613 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1622 ----------------------------------------------------------------------------
1624 .. explicar la cantidad de cosas que hay (que son muchas) y dar algunos
1630 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.
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`_.
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>`.
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
1666 Estas son las dos grandes familias de recolectores, también conocidos como
1667 `conteo de referencias`_ (directa) y *traicing garbage collection*
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).
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
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.
1699 .. _ref_gc_concurrent:
1701 Concurrente / *stop-the-world*
1702 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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.
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.
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.
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.
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.
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
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.
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/)
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
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
1768 Implementación de Bohem GC
1769 http://www.hpl.hp.com/personal/Hans_Boehm/gc/gcdescr.html
1771 Interfaz de Bohem GC
1772 http://www.hpl.hp.com/personal/Hans_Boehm/gc/gcinterface.html
1775 .. include:: links.rst
1777 .. vim: set ts=3 sts=3 sw=3 et tw=78 :