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 ============================================================================
16 ----------------------------------------------------------------------------
18 *Recolección de basura* se refiere a la recuperación automática de memoria
19 del *heap* [#gcheap]_ una vez que el programa ha dejado de hacer referencia
20 a ella (y por lo tanto, ha dejado de utilizarla).
22 .. [#gcheap] *Heap* es un área de memoria que se caracteriza por ser
23 dinámica (a diferencia del área de memoria estática que está disponible
24 durante toda la ejecución de un programa). Un programa puede reservar
25 memoria en tiempo de ejecución según sea necesario y liberarla cuando ya
26 no la necesita. A diferencia del *stack*, la duración de la *reserva* no
27 está atada a un bloque de código.
29 A medida que el tiempo pasa, cada vez los programas son más complejos y es
30 más compleja la administración de memoria. Uno de los aspectos más
31 importantes de un recolector de basura es lograr un mayor nivel de
32 abstracción y modularidad, dos conceptos claves en la ingeniería de
33 software [JOLI96]_. En particular, al diseñar o programar bibliotecas, de
34 no haber un recolector de basura, **la administración de memoria pasa a ser
35 parte de la interfaz**, lo que produce que los módulos tengan un mayor
36 grado de acoplamiento.
38 Además hay una incontable cantidad de problemas asociados al manejo
39 explícito de memoria que simplemente dejan de existir al utilizar un
40 recolector de basura. Por ejemplo, los errores en el manejo de memoria
41 (como *buffer overflows* [#gcbuff]_ o *dangling pointers* [#gcdang]_) son
42 la causa más frecuente de problemas de seguridad [BEZO06]_.
44 .. [#gcbuff] Un *buffer overflow* (*desbordamiento de memoria* en
45 castellano) se produce cuando se copia un dato a un área de memoria que
46 no es lo suficientemente grande para contenerlo. Esto puede producir que
47 el programa sea abortado por una violación de segmento, o peor,
48 sobreescribir un área de memoria válida, en cuyo caso los resultados son
51 .. [#gcdang] Un *dangling pointer* (*puntero colgante* en castellano) es un
52 puntero que apunta a un área de memoria inválida. Ya sea porque el
53 elemento apuntado no es el mismo tipo o porque la memoria ya ha sido
54 liberada. Al ser desreferenciado, los resultados son impredecibles, el
55 programa podría abortarse por una violación de segmento o podrían pasar
56 peores cosas si el área de memoria fue realocada para almacenar otro
59 La recolección de basura nació junto a Lisp_ a finales de 1950 y en los
60 siguientes años estuvo asociada principalmente a lenguajes funcionales,
61 pero en la actualidad está presente en prácticamente todos los lenguajes de
62 programación, de alto o bajo nivel, aunque sea de forma opcional. En los
63 últimos 10 años tuvo un gran avance, por la adopción en lenguajes de
64 desarrollo rápido utilizados mucho en el sector empresarial, en especial
65 Java_, que fue una plataforma de facto para la investigación y desarrollo
66 de recolectores de basura (aunque no se limitaron a este lenguaje las
69 En las primeras implementaciones de recolectores de basura la penalización
70 en la eficiencia del programa se volvía prohibitiva para muchas
71 aplicaciones. Es por esto que hubo bastante resistencia a la utilización de
72 recolectores de basura, pero el avance en la investigación fue haciendo que
73 cada vez sea una alternativa más viable al manejo manual de memoria,
74 incluso para apliaciones con altos requerimientos de eficiencia. En la
75 actualidad un programa que utiliza un recolector moderno puede ser
76 comparable en eficiencia con uno que utiliza un esquema manual. En
77 particular, si el programa fue diseñado con el recolector de basura en
78 mente en ciertas circunstancias puede ser incluso más eficiente que uno que
79 hace manejo explícito de la memoria. Muchos recolectores mejoran la
80 localidad de referencia, haciendo que el programa tenga un mejor
81 comportamiento con el caché y la memoria virtual.
83 El recolector de basura debe tener un comportamiento correcto y predecible
84 para que sea útil, si el programador no puede confiar en el recolector
85 de basura, éste se vuelve más un problema que una solución, porque
86 introduce nuevos puntos de falla en los programas, y lo que es peor,
87 puntos de falla no controlados por el programador, volviendo mucho más
88 difícil la búsqueda de errores.
94 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
96 Los programas pueden hacer uso principalmente de 4 áreas de memoria:
99 Se trata de la memoria más básica de una computadora. Es el área de
100 memoria en la que puede operar realmente el procesador, es extremadamente
101 escasa y generalmente su uso es administrado por el lenguaje de
102 programación (o compilador más específicamente). Excepto en situaciones
103 muy particulares, realizando tareas de muy bajo nivel, un programador
104 nunca manipula los registros explícitamente.
106 Área de memoria estática:
107 Es la forma de memoria más simple que un programador utiliza
108 explícitamente. En general las variables globales se almacenan en este
109 área, que es parte inherente del programa y está disponible durante toda
110 su ejecución, por lo tanto nunca cambia su capacidad en tiempo de
111 ejecución. Es la forma más básica de administrar memoria, pero tiene una
112 limitación fundamental: **el tamaño de la memoria tiene que ser conocido
113 en tiempo de compilación**. Los primeros lenguajes de programación solo
114 contaban con este tipo de memoria (además de los registros del
118 Los primeros lenguajes de programación que hicieron uso de una pila
119 aparecieron en el año 1958 (Algol-58 y Atlas Autocode) y fueron los
120 primeros en introducir estructura de bloques, almacenando las
121 variables locales a estos bloques utilizando una pila [JOLI96]_.
122 Esto permite utilizar recursividad y tener un esquema simple de memoria
123 dinámica. Sin embargo este esquema es muy limitado porque el orden de
124 reserva y liberación de memoria tiene que estar bien establecido. Una
125 celda [#gccelda]_ alocada antes que otra nunca puede ser liberada antes
128 .. [#gccelda] En general en la literatura se nombra a una porción de
129 memoria alocada individualmente *celda*, *nodo* u *objeto*
130 indistintamente. En este trabajo se utilizará la misma nomenclatura
131 (haciendo mención explícita cuando alguno de estos términos se
132 refiera a otra cosa, como al nodo de una lista o a un objeto en el
133 sentido de programación orientada a objetos).
136 A diferencia del *stack*, el *heap* provee un área de memoria que puede
137 ser obtenida dinámicamente pero sin limitaciones de orden. Es el tipo de
138 memoria más flexible y por lo tanto el más complejo de administrar; razón
139 por la cual existen los recolectores de basura.
141 La recolección de basura impone algunas restricciones sobre la manera de
142 utilizar el *heap*. Debido a que un recolector de basura debe ser capaz de
143 determinar el grafo de conectividad de la memoria en uso, es necesario que
144 el programa siempre tenga alguna referencia a las celdas activas en los
145 registros, memoria estática o *stack* (normalmente denominado *root set*).
147 Esto implica que una celda sea considerada basura si y sólo si no puede ser
148 alcanzada a través del grafo de conectividad que se comienza a recorrer
149 desde el *root set*. Por lo tanto, una celda está *viva* si y sólo si su
150 dirección de memoria está almacenada en una celda *raíz* (parte del *root
151 set*) o si está almacenada en otra celda *viva* del *heap*.
153 Expresado más formalmente, dada la relación :math:`M \to N`, donde
154 :math:`M` es una celda del *heap* o parte del *root set* y :math:`N` es una
155 celda del *heap*, definida como:
159 M \to N \Longleftrightarrow M \text{ almacena un puntero a } N
161 El conjunto de celdas vivas (o *live set*) queda determinado por:
165 vivas = \left\lbrace N \in Celdas \big/
166 ( \exists r \in Raices / r \to N ) \vee (\exists M \in vivas / M \to N )
169 Cabe aclarar que esta es una definición conceptual, asumiendo que el
170 programa siempre limpia una dirección de memoria almacenada en el *root
171 set* o una celda del *heap* cuando la celda a la que apunta no va a ser
172 utilizada nuevamente. Esto no es siempre cierto y los falsos positivos que
173 esto produce se conoce como un tipo de pérdida de memoria (que es posible
174 incluso al utilizar un recolector de basura) llamada pérdida de memoria
175 *lógica*. Esto puede no ser evitable (incluso cuando el programador no
176 cometa errores) en lenguajes de programación que requieran un recolector de
179 Por último, siendo que el recolector de basura es parte del programa de
180 forma indirecta, es común ver en la literatura que se direfencia entre
181 2 partes del programa, el recolector de basura y el programa en sí. Dado
182 que para el recolector de basura, lo único que interesa conocer del
183 programa en sí son los cambios al grafo de conectividad de las celdas,
184 normalmente se lo llama *mutator* (mutador).
188 .. _ref_gc_intro_mark:
190 Recorrido del grafo de conectividad
191 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
193 El problema de encontrar las celdas *vivas* de un programa se reduce a recorrer
194 un grafo dirigido. El grafo se define como:
200 Donde :math:`V` es el conjunto de vértices, dado por las celdas de memoria
201 y :math:`A` es un conjunto de pares ordenados (aristas), dado por la
202 relación :math:`M \rightarrow N` (es decir, los punteros).
204 El grafo comienza a recorrerse desde el *root set* y todos los vértices que
205 fueron visitados componen el *live set*; el resto de los vértices son
208 Más formalmente, Definimos:
211 secuencia de vértices tal que cada uno de los vértices tiene una arista
212 al próximo vértice en la secuencia. Todo camino finito tiene un *vértice
213 inicial* y un *vértice final* (llamados en conjunto *vértices
214 terminales*). Cualquier vértice no terminal es denominado *vértice
219 \underset{v_1 \rightarrow v_N}{C} = \left\lbrace
220 v_1, \dotsc, v_N \in V \big/ \underset{i \in [1,N-1]}{\forall v_i}
221 \exists (v_i \to v_{i+1}) \in A
225 decimos que :math:`M` está *conectado* a :math:`N` si y sólo si existe un
226 camino de :math:`M` a :math:`N`.
230 M \mapsto N \Longleftrightarrow \exists \underset{M \to N}{C} \in G
233 el conjunto de celdas *vivas* está dado por todos los vértices
234 (:math:`v`) del grafo para los cuales existe una raíz en el *root set*
235 que esté conectada a él.
239 Live \thickspace set = \left\lbrace v \in V \big/
240 \left( \exists r \in Root \thickspace set \big/ r \mapsto v \right)
244 la basura, o celdas *muertas*, quedan determinadas entonces por todas las
245 celdas del *heap* que no son parte del *live set*.
249 Basura = V - Live \thickspace set
251 Esto es, efectivamente, una partición del *heap* (ver figura
252 :vref:`fig:gc-heap-parts`).
255 .. fig:: fig:gc-heap-parts
257 Distintas partes de la memoria, incluyendo relación entre *basura*,
258 *live set*, *heap* y *root set*.
265 node [ shape = record, width = 0, height = 0 ];
267 subgraph cluster_heap {
273 subgraph cluster_live {
286 subgraph cluster_garbage {
291 node [ style = filled, fillcolor = white ];
296 subgraph cluster_root {
301 node [ style = filled, fillcolor = gray96 ];
305 r0 -> h1 -> h2 -> h5;
306 r1 -> h5 -> h6 -> h1;
313 Al proceso de visitar los vértices *conectados* desde el *root set* se lo
314 denomina *marcado*, *fase de marcado* o *mark phase* en inglés, debido
315 a que es necesario marcar los vértices para evitar visitar 2 veces el mismo
316 nodo en casos de que el grafo contenga ciclos [#gccycle]_. De forma similar
317 a la búsqueda, que puede realizarse *primero a lo ancho* (*breadth-first*)
318 o *primero a lo alto* (*depth-first*) del grafo, el marcado de un grafo
319 también puede realizarse de ambas maneras. Cada una podrá o no tener
320 efectos en la eficiencia, en particular dependiendo de la aplicación puede
321 convenir uno u otro método para lograr una mejor localidad de referencia
322 y de esta manera tener un mejor comportamiento de la memoria virtual o del
325 .. [#gccycle] Un ciclo es un camino donde el *vértice inicial* es el mismo
326 que el *vértice final*. Por lo tanto, los *vértices terminales* son
327 completamente arbitrarios, ya que cualquier *vértice interior* puede ser
328 un *vértice terminal*.
330 Un algoritmo simple (recursivo) de marcado *primero a lo alto* puede ser
331 el siguiente (asumiendo que partimos con todos los vértices sin marcar)
337 for (src, dst) in v.edges
340 function mark_phase() is
341 foreach r in root_set
344 .. [#gcpseudo] Para presentar los algoritmos se utiliza una forma simple de
345 pseudo-código. El pseudo-código se escribe en inglés para que pueda ser
346 más fácilmente contrastado con la literatura, que está en inglés. Para
347 diferenciar posiciones de memoria y punteros de las celdas en sí, se usa
348 la misma sintaxis que C, ``r*`` denota una referencia o puntero y ``*r``
349 denota "objeto al que apunta ``r``\ ". Se sobreentiende que ``r = o``
350 siempre toma la dirección de memoria de ``o``.
352 Una vez concluido el marcado, sabemos que todos los vértices con la marca
353 son parte del *live set* y que todos los vértices no marcados son *basura*.
354 Esto es conocido también como **abstracción bicolor**, dado que en la
355 literatura se habla muchas veces de *colorear* las celdas. En general, una
356 celda sin marcar es de color blanco y una marcada de color negro.
358 Puede observarse un ejemplo del algoritmo en la figura
359 :vref:`fig:gc-mark-1`, en la cual se marca el sub-grafo apuntando por
360 ``r0``. Luego se marca el sub-grafo al que apunta ``r1`` (ver figura
361 :vref:`fig:gc-mark-2`), concluyendo con el marcado del grafo completo,
362 dejando sin marcar solamente las celdas *basura* (en blanco).
365 .. fig:: fig:gc-mark-1
367 Ejemplo de marcado del grafo de conectividad (parte 1).
371 Se comienza a marcar el grafo por la raíz r0.
378 node [ shape = record, width = 0, height = 0];
379 edge [ color = gray40 ];
381 subgraph cluster_all {
384 label = "root\nset|<r0> r0\n*|<r1> r1",
390 node [ style = filled, fillcolor = gray25, fontcolor = white ];
394 root:r0 -> h1 [ style = bold, color = black ];
395 h1 -> h2 -> h5 -> h1;
404 Luego de marcar el nodo ``h1``, se procede al ``h2``.
411 node [ shape = record, width = 0, height = 0 ];
412 edge [ color = gray40 ];
414 subgraph cluster_all {
417 label = "root\nset|<r0> r0\n*|<r1> r1",
423 node [ style = filled, fillcolor = gray25, fontcolor = white ];
427 root:r0 -> h1 [ color = gray10 ];
428 h1 -> h2 [ style = bold, color = black ];
438 Luego sigue el nodo h5.
445 node [ shape = record, width = 0, height = 0 ];
446 edge [ color = gray40 ];
448 subgraph cluster_all {
451 label = "root\nset|<r0> r0\n*|<r1> r1",
457 node [ style = filled, fillcolor = gray25, fontcolor = white ];
461 root:r0 -> h1 [ color = gray10 ];
462 h1 -> h2 [ color = gray10 ];
463 h2 -> h5 [ style = bold, color = black ];
472 .. fig:: fig:gc-mark-2
474 Ejemplo de marcado del grafo de conectividad (parte 2).
478 El nodo h5 tiene una arista al h1, pero el h1 ya fue visitado, por lo
479 tanto no se visita nuevamente.
486 node [ shape = record, width = 0, height = 0 ];
487 edge [ color = gray40 ];
489 subgraph cluster_all {
492 label = "root\nset|<r0> r0\n*|<r1> r1",
498 node [ style = filled, fillcolor = gray25, fontcolor = white ];
502 root:r0 -> h1 [ color = gray10 ];
503 h1 -> h2 [ color = gray10 ];
504 h2 -> h5 [ color = gray10 ];
505 h5 -> h1 [ style = bold, color = black ];
514 Se concluye el marcado del sub-grafo al que conecta r0, se procede
515 a marcar el sub-grafo al que conecta r1, marcando al nodo h6.
522 node [ shape = record, width = 0, height = 0 ];
523 edge [ color = gray40 ];
525 subgraph cluster_all {
528 label = "root\nset|<r0> r0|<r1> r1\n*",
534 node [ style = filled, fillcolor = gray25, fontcolor = white ];
538 root:r0 -> h1 [ color = gray10 ];
539 h1 -> h2 [ color = gray10 ];
540 h2 -> h5 [ color = gray10 ];
541 h5 -> h1 [ color = gray10 ];
542 root:r1 -> h6 [ style = bold, color = black ];
551 El nodo h6 tiene una arista al h2, pero éste ya fue marcado por lo
552 que no se vuelve a visitar. No hay más raíces, se finaliza el marcado
560 node [ shape = record, width = 0, height = 0 ];
561 edge [ color = gray40 ];
563 subgraph cluster_all {
566 label = "root\nset|<r0> r0|<r1> r1\n*",
572 node [ style = filled, fillcolor = gray25, fontcolor = white ];
576 root:r0 -> h1 [ color = gray10 ];
577 h1 -> h2 [ color = gray10 ];
578 h2 -> h5 [ color = gray10 ];
579 h5 -> h1 [ color = gray10 ];
580 root:r1 -> h6 [ color = gray10 ];
581 h6 -> h2 [ style = bold, color = black ];
589 .. _ref_gc_intro_tricolor:
592 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
594 Muchos algoritmos utilizan tres colores para realizar el marcado. El tercer
595 color, gris generalmente, indica que una celda debe ser visitada. Esto
596 permite algoritmos :ref:`concurrentes <ref_gc_concurrent>`
597 e :ref:`incrementales <ref_gc_inc>`, además de otro tipo de optimizaciones.
598 Entonces, lo que plantea esta abtracción es una nueva partición del heap al
599 momento de marcar, esta vez son 3 porciones: blanca, gris y negra.
601 Al principio todas las celdas se pintan de blanco, excepto el *root set*
602 que se punta de gris. Luego se van obteniendo celdas del conjunto de las
603 grises y se las pinta de negro, pintando sus hijas directas de gris.
605 Una vez que no hay más celdas grises, tenemos la garantía de que las celdas
606 negras serán el *live set* y las celdas blancas *basura*. Esto se debe
607 a que siempre se mantiene esta invariante: **ninguna celda negra apunta
608 directamente a una celda blanca**. Las celdas blancas siempre son apuntadas
609 por celdas blancas o grises. Entonces, siempre que el conjunto de celdas
610 grises sea vacío, no habrán celdas negras conectadas a blancas, siendo las
611 celdas blancas *basura*.
613 El algoritmo básico para marcar con tres colores es el siguiente (asumiendo
614 que todas las celdas parten pintadas de blanco, es decir, el conjunto
615 blanco contiene todas las celdas de memoria y los conjuntos negro y gris
618 function mark_phase() is
619 foreach r in root_set
621 while not gray_set.empty()
624 for (src, dst) in v.edges
629 Es simple notar que este algoritmo es naturalmente no recursivo, lo que de
630 por sí ya presenta una ventaja sobre el marcado *bicolor*.
634 .. _ref_gc_intro_services:
637 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
639 En general todos los algoritmos de recolección de basura utilizan servicios
640 de una capa inferior [#gclowlayer]_ y proveen servicios a una capa superior
643 .. [#gclowlayer] En general estos servicios están provistos directamente
644 por el sistema operativo pero también pueden estar dados por un
645 administrador de memoria de bajo nivel (o *low level allocator* en
648 .. [#gchilayer] En general estos servicios son utilizados directamente por
649 el lenguaje de programación, pero pueden ser utilizados directamente por
650 el usuario del lenguaje si éste interatúa con el recolector, ya sea por
651 algún requerimiento particular o porque el lenguaje no tiene soporte
652 diercto de recolección de basura y el recolector está implementado como
653 una biblioteca de usuario.
655 A continuación se presentan las primitivas en común que utilizan todos los
656 recolectores a lo largo de este documento.
658 Servicios utilizados por el recolector son los siguientes:
660 :math:`alloc() \to cell`:
661 obtiene una nueva celda de memoria. El mecanismo por el cual se obtiene
662 la celda es indistinto para esta sección, puede ser de una lista libre,
663 puede ser de un administrador de memoria de más bajo nivel provisto por
664 el sistema operativo o la biblioteca estándar de C (``malloc()``), etc.
665 Cómo organizar la memoria es un área de investigación completa y si bien
666 está estrechamente relacionada con la recolección de basura, en este
667 trabajo no se prestará particular atención a este aspecto (salvo casos
668 donde el recolector impone una cierta organización de memoria en el *low
669 level allocator*). Por simplicidad también asumiremos (a menos que se
670 indique lo contrario) que las celdas son de tamaño fijo. Esta restricción
671 normalmente puede ser fácilmente relajada (en los recolectores que la
675 libera una celda que ya no va a ser utilizada. La celda liberada debe
676 haber sido obtenida mediante ``alloc()``.
678 Y los servicios básicos proporcionados por el recolector son los
681 :math:`new() \to cell`:
682 obtiene una celda de memoria para ser utilizada por el programa.
684 :math:`update(ref, cell)`:
685 notifica al recolector que la referencia :math:`ref` ahora apunta
686 a :math:`cell`. Visto más formalmente, sería análogo a decir que hubo un
687 cambio en la conectividad del grafo: la arista :math:`src \to old` cambia
688 por :math:`src \to new` (donde :math:`src` es la celda que contiene la
689 referencia :math:`ref`, :math:`old` es la celda a la que apunta la
690 referencia :math:`ref` y :math:`new` es el argumento :math:`cell`). Si
691 :math:`cell` es ``null``, sería análogo a informar que se elimina la
692 arista :math:`src \to old`.
695 este servicio, según el algoritmo, puede ser utilizado para informar un
696 cambio en la conectividad del grafo, la eliminación de una arista
697 (análogo a :math:`update(ref, null)` pero sin proporcionar información
698 sobre la arista a eliminar). Esto es generalmente útil solo en
699 :ref:`conteo de referencias <ref_gc_rc>`. Para otros recolectores puede
700 significar que el usuario asegura que no hay más referencias a esta
701 celda, es decir, análogo a eliminar el conjunto de aristas
702 :math:`\big\lbrace (v, w) \in A , v \in Live \thickspace set , w \in
703 Live \thickspace set \big/ w = cell`.
706 indica al recolector que debe hacer un análisis del grafo de conectividad
707 en busca de *basura*. Generalmente este servicio es invocado por el
708 propio recolector cuando no hay más celdas reciclables.
710 No todos los servicios son implementados por todos los recolectores, pero
711 son lo suficientemente comunes como para describirlos de forma general en
712 esta sección. Algunos son principalmente ideados para uso interno del
713 recolector, aunque en ciertas circunstancias pueden ser utilizados por el
722 ----------------------------------------------------------------------------
724 En la literatura se encuentran normalmente referencias a 3 algoritmos
725 clásicos, que son utilizados generalmente como bloques básicos para
726 construir recolectores más complejos. Se presentan las versiones históricas
727 más simples a fin de facilitar la comprensión conceptual.
733 Conteo de referencias
734 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
736 Se trata del algoritmo más antiguo de todos, implementado por primera vez
737 por `John McCarthy`_ para Lisp_ a finales de 1950. Se trata de un método
738 :ref:`directo <ref_gc_direct>` e :ref:`incremental <ref_gc_inc>` por
739 naturaleza, ya que distribuye la carga de la recolección de basura durante
740 toda la ejecución del programa, cada vez que el *mutator* cambia la
741 conectividad de algún nodo del grafo de conectividad.
743 El método consiste en tener un contador asociado a cada celda que contenga
744 la cantidad de celdas **vivas** que apuntan a ésta. Es decir, es la
745 cardinalidad del conjunto de aristas que tienen por destino a la celda.
746 Formalmente podemos definir el contador :math:`rc(v)` (de *reference
747 counter* en inglés) de la siguiente manera:
753 (v_1, v_2) \in A \big/
754 v_1 \in Live \thickspace set \cup Root \thickspace set
759 El *mutator* entonces debe actualizar este contador cada vez que el grafo
760 de conectividad cambia, es decir, cada vez que se agrega, modifica
761 o elimina una arista del grafo (o visto de una forma más cercana al código,
762 cada vez que se agrega, modifica o elimina un puntero).
764 Esta invariante es fundamental para el conteo de referencias, porque se
765 asume que si el contador es 0 entonces el *mutator* no tiene ninguna
766 referencia a la celda y por lo tanto es *basura*:
770 rc(v) = 0 \Rightarrow v \in Basura
772 Para mantener esta invariante el *mutator*, cada vez que cambia un puntero
773 debe decrementar en 1 el contador de la celda a la que apuntaba
774 antiguamente e incrementar en 1 el contador de la celda a la que apunta
775 luego de la modificación. Esto asegura que la invariante se mantenga
776 durante toda la ejecución del programa. Si al momento de decrementar un
777 contador éste queda en 0, la celda asociada debe liberarse de forma de
778 poder ser reciclada. Esto implica que si esta celda almacena punteros, los
779 contadores de las celdas apuntadas deben ser decrementados también, porque
780 solo deben almacenarse en el contador las aristas del *live set* para
781 mantener la invariante. De esto puede resultar que otros contadores de
782 referencia queden en 0 y más celdas sean liberadas. Por lo tanto,
783 teóricamente la complejidad de eliminar una referencia puede ser
784 :math:`O(\lvert Live \thickspace set \rvert)` en el peor caso.
786 Las primitivas implementadas para este tipo de recolector son las
787 siguientes (acompañadas de una implementación básica)::
796 function del(cell) is
797 cell.rc = cell.rc - 1
799 foreach child* in cell.children
803 function update(ref*, cell) is
804 cell.rc = cell.rc + 1
810 .. _ref_gc_rc_cycles:
815 El conteo de referencias tiene, sin embargo, un problema fundamental:
816 **falla con estructuras cíclicas**. Esto significa que siempre que haya un
817 ciclo en el grafo de conectividad, hay una pérdida de memoria potencial en
818 el programa. Un ciclo es un camino :math:`\underset{v \to v}{C}`, es decir,
819 el *vértice inicial* es el mismo que el *vértice final*.
821 Cuando esto sucede, las celdas que participan del ciclo tienen siempre su
822 contador mayor que 0, sin embargo puede no haber ningún elemento del *root
823 set* que apunte a una celda dentro del ciclo, por lo tanto el ciclo es
824 *basura* (al igual que cualquier otra celda que sea referenciada por el
825 ciclo pero que no tenga otras referencias externas) y sin embargo los
826 contadores no son 0. Los ciclos, por lo tanto, *rompen* la invariante del
827 conteo de referencia.
829 Hay formas de solucionar esto, pero siempre recaen en un esquema que va por
830 fuera del conteo de referencias puro. En general los métodos para
831 solucionar esto son variados y van desde realizar un marcado del subgrafo
832 para detectar nodos hasta tener otro recolector completo de *emergencia*,
833 pasando por tratar los ciclos como un todo contar las referencias al ciclo
834 completo en vez de a cada celda en particular.
836 Incluso con este problema, el conteo de referencia sin ningún tipo de
837 solución en cuanto a la detección y recolección de ciclos fue utilizado en
838 muchos lenguajes de programación sin que su necesidad sea tan evidente. Por
839 ejemplo Python_ agregó recolección de ciclos en la versión 2.0 [NAS00]_
840 (liberada en octubre de 2000) y PHP_ recién agrega detección de ciclos en
841 la versión 5.3 (todavía no liberada al momento de escribir este documento)
846 .. _ref_gc_rc_example:
851 A continuación se presenta un ejemplo gráfico para facilitar la comprensión
852 del algoritmo. Por simplicidad se asumen celdas de tamaño fijo con dos
853 punteros, ``left`` (``l``) y ``right`` (``r``) y se muestra el contador de
854 referencias abajo del nombre de cada celda. Se parte con una pequeña
855 estructura ya construída y se muestra como opera el algoritmo al eliminar
856 o cambiar una referencia (cambios en la conectividad del grafo). En un
857 comienzo todas las celdas son accesibles desde el *root set* por lo tanto
858 son todas parte del *live set*.
861 .. fig:: fig:gc-rc-rm-1
863 Eliminación de la referencia ``r0`` :math:`\to` ``h1`` (parte 1).
867 Estado inicial del grafo de conectividad.
874 edge [ color = gray40 ];
884 subgraph cluster_all {
887 label = "root\nset|<r0> r0|<r1> r1",
893 h1 [ label = "h1\n1|<l> l|<r> r" ];
894 h2 [ label = "h2\n2|<l> l|<r> r" ];
895 h3 [ label = "h3\n3|<l> l|<r> r" ];
896 h4 [ label = "h4\n1|<l> l|<r> r" ];
897 h5 [ label = "h5\n1|<l> l|<r> r" ];
898 h6 [ label = "h6\n1|<l> l|<r> r" ];
914 Al ejecutarse ``update(r0, null)``, se comienza por visitar la celda
922 edge [ color = gray40 ];
932 subgraph cluster_all {
935 label = "root\nset|<r0> r0\n*|<r1> r1",
941 h1 [ label = "h1\n1|<l> l|<r> r" ];
942 h2 [ label = "h2\n2|<l> l|<r> r" ];
943 h3 [ label = "h3\n3|<l> l|<r> r" ];
944 h4 [ label = "h4\n1|<l> l|<r> r" ];
945 h5 [ label = "h5\n1|<l> l|<r> r" ];
946 h6 [ label = "h6\n1|<l> l|<r> r" ];
948 root:r0 -> h1 [ style = bold, color = black ];
962 Se decrementa el contador de ``h1`` quedando en 0 (pasa a ser
963 *basura*). Se elimina primero ``h1.l`` y luego ``h1.r``.
970 edge [ color = gray40 ];
980 subgraph cluster_all {
983 label = "root\nset|<r0> r0\n*|<r1> r1",
990 node [ fillcolor = white, fontcolor = black ];
994 h1 [ label = "h1\n0|<l> l|<r> r" ];
995 h2 [ label = "h2\n2|<l> l|<r> r" ];
996 h3 [ label = "h3\n3|<l> l|<r> r" ];
997 h4 [ label = "h4\n1|<l> l|<r> r" ];
998 h5 [ label = "h5\n1|<l> l|<r> r" ];
999 h6 [ label = "h6\n1|<l> l|<r> r" ];
1001 root:r0 -> h1 [ style = invis ];
1003 h1:l -> h2 [ style = bold, color = black ];
1014 .. fig:: fig:gc-rc-rm-2
1017 Eliminación de la referencia ``r0`` :math:`\to` ``h1`` (parte 2).
1021 Se decrementa el contador de ``h2`` pero no queda en 0 (permanece en
1029 edge [ color = gray40 ];
1039 subgraph cluster_all {
1042 label = "root\nset|<r0> r0\n*|<r1> r1",
1049 node [ fillcolor = white, fontcolor = black ];
1053 h1 [ label = "h1\n0|<l> l|<r> r" ];
1054 h2 [ label = "h2\n1|<l> l|<r> r" ];
1055 h3 [ label = "h3\n3|<l> l|<r> r" ];
1056 h4 [ label = "h4\n1|<l> l|<r> r" ];
1057 h5 [ label = "h5\n1|<l> l|<r> r" ];
1058 h6 [ label = "h6\n1|<l> l|<r> r" ];
1060 root:r0 -> h1 [ style = invis ];
1062 h1:l -> h2 [ style = invis ];
1063 h1:r -> h3 [ style = bold, color = black ];
1074 El contador de ``h3`` tampoco queda en 0, sigue en el *live set*.
1081 edge [ color = gray40 ];
1091 subgraph cluster_all {
1094 label = "root\nset|<r0> r0|<r1> r1",
1101 node [ fillcolor = white, fontcolor = black ];
1105 h1 [ label = "h1\n0|<l> l|<r> r" ];
1106 h2 [ label = "h2\n1|<l> l|<r> r" ];
1107 h3 [ label = "h3\n2|<l> l|<r> r" ];
1108 h4 [ label = "h4\n1|<l> l|<r> r" ];
1109 h5 [ label = "h5\n1|<l> l|<r> r" ];
1110 h6 [ label = "h6\n1|<l> l|<r> r" ];
1112 root:r0 -> h1 [ style = invis ];
1114 h1:l -> h2 [ style = invis ];
1115 h1:r -> h3 [ style = invis ];
1125 Se comienza por eliminar la referencia de ``r0`` a ``h1``, que determina
1126 que ``h1`` se convirtió en *basura* (ver figura :vref:`fig:gc-rc-rm-1`). Esto
1127 conduce al decremento del contador de ``h2`` y ``h3`` que permanecen en el
1128 *live set* ya que sus contadores siguen siendo mayores a 0 (ver figura
1129 :vref:`fig:gc-rc-rm-2`).
1132 .. fig:: fig:gc-rc-up-1
1134 Cambio en la referencia ``h2.l`` :math:`\to` ``h2`` a ``h2.l``
1135 :math:`\to` ``h5`` (parte 1).
1139 Comienza ``update(h3.l, h5)``, se incrementa el contador de ``h5``.
1146 edge [ color = gray40 ];
1156 subgraph cluster_all {
1159 label = "root\nset|<r0> r0|<r1> r1",
1166 node [ fillcolor = white, fontcolor = black ];
1170 h1 [ label = "h1\n0|<l> l|<r> r" ];
1171 h2 [ label = "h2\n1|<l> l|<r> r" ];
1172 h3 [ label = "h3\n2|<l> l\n*|<r> r" ];
1173 h4 [ label = "h4\n1|<l> l|<r> r" ];
1174 h5 [ label = "h5\n2|<l> l|<r> r" ];
1175 h6 [ label = "h6\n1|<l> l|<r> r" ];
1177 root:r0 -> h1 [ style = invis ];
1178 h1:l -> h2 [ style = invis ];
1179 h1:r -> h3 [ style = invis ];
1184 h3:l -> h5 [ style = dotted, color = black ];
1192 Luego se procede a visitar las hijas de ``h3``, comenzando por ``h2``.
1199 edge [ color = gray40 ];
1209 subgraph cluster_all {
1212 label = "root\nset|<r0> r0|<r1> r1",
1219 node [ fillcolor = white, fontcolor = black ];
1223 h1 [ label = "h1\n0|<l> l|<r> r" ];
1224 h2 [ label = "h2\n1|<l> l|<r> r" ];
1225 h3 [ label = "h3\n2|<l> l\n*|<r> r" ];
1226 h4 [ label = "h4\n1|<l> l|<r> r" ];
1227 h5 [ label = "h5\n2|<l> l|<r> r" ];
1228 h6 [ label = "h6\n1|<l> l|<r> r" ];
1230 root:r0 -> h1 [ style = invis ];
1231 h1:l -> h2 [ style = invis ];
1232 h1:r -> h3 [ style = invis ];
1236 h3:l -> h2 [ style = bold, color = black ];
1237 h3:l -> h5 [ style = dotted, color = black ];
1245 Se decrementa el contador de ``h2`` y queda en 0 (pasa a ser
1246 *basura*). Se eliminan las referencias a las hijas.
1253 edge [ color = gray40 ];
1263 subgraph cluster_all {
1266 label = "root\nset|<r0> r0|<r1> r1",
1273 node [ fillcolor = white, fontcolor = black ];
1277 h1 [ label = "h1\n0|<l> l|<r> r" ];
1278 h2 [ label = "h2\n1|<l> l|<r> r" ];
1279 h3 [ label = "h3\n2|<l> l\n*|<r> r" ];
1280 h4 [ label = "h4\n1|<l> l|<r> r" ];
1281 h5 [ label = "h5\n2|<l> l|<r> r" ];
1282 h6 [ label = "h6\n1|<l> l|<r> r" ];
1284 root:r0 -> h1 [ style = invis ];
1285 h1:l -> h2 [ style = invis ];
1286 h1:r -> h3 [ style = invis ];
1288 h2:l -> h4 [ style = bold, color = black ];
1290 h3:l -> h2 [ style = invis ];
1291 h3:l -> h5 [ style = dotted, color = black ];
1298 .. fig:: fig:gc-rc-up-2
1300 Cambio en la referencia ``h2.l`` :math:`\to` ``h2`` a ``h2.l``
1301 :math:`\to` ``h5`` (parte 2).
1305 Se decrementa el contador de ``h4`` quedando en 0, pasa a ser
1306 *basura*. Se continúa con ``h5``.
1313 edge [ color = gray40 ];
1323 subgraph cluster_all {
1326 label = "root\nset|<r0> r0|<r1> r1",
1333 node [ fillcolor = white, fontcolor = black ];
1337 h1 [ label = "h1\n0|<l> l|<r> r" ];
1338 h2 [ label = "h2\n1|<l> l|<r> r" ];
1339 h3 [ label = "h3\n2|<l> l\n*|<r> r" ];
1340 h4 [ label = "h4\n0|<l> l|<r> r" ];
1341 h5 [ label = "h5\n2|<l> l|<r> r" ];
1342 h6 [ label = "h6\n1|<l> l|<r> r" ];
1344 root:r0 -> h1 [ style = invis ];
1345 h1:l -> h2 [ style = invis ];
1346 h1:r -> h3 [ style = invis ];
1348 h2:l -> h4 [ style = invis ];
1349 h2:r -> h5 [ style = bold, color = black ];
1350 h3:l -> h2 [ style = invis ];
1351 h3:l -> h5 [ style = dotted, color = black ];
1359 Se decrementa el contador de ``h5`` pero sigue siendo mayor que 0.
1366 edge [ color = gray40 ];
1376 subgraph cluster_all {
1379 label = "root\nset|<r0> r0|<r1> r1",
1386 node [ fillcolor = white, fontcolor = black ];
1390 h1 [ label = "h1\n0|<l> l|<r> r" ];
1391 h2 [ label = "h2\n1|<l> l|<r> r" ];
1392 h3 [ label = "h3\n2|<l> l\n*|<r> r" ];
1393 h4 [ label = "h4\n0|<l> l|<r> r" ];
1394 h5 [ label = "h5\n1|<l> l|<r> r" ];
1395 h6 [ label = "h6\n1|<l> l|<r> r" ];
1397 root:r0 -> h1 [ style = invis ];
1398 h1:l -> h2 [ style = invis ];
1399 h1:r -> h3 [ style = invis ];
1401 h2:l -> h4 [ style = invis ];
1402 h2:r -> h5 [ style = invis ];
1403 h3:l -> h5 [ style = bold, color = black ];
1404 h3:l -> h2 [ style = invis ];
1412 Se termina por actualizar la referencia de ``h3.l`` para que apunte
1420 edge [ color = gray40 ];
1430 subgraph cluster_all {
1433 label = "root\nset|<r0> r0|<r1> r1",
1440 node [ fillcolor = white, fontcolor = black ];
1444 h1 [ label = "h1\n0|<l> l|<r> r" ];
1445 h1 [ label = "h1\n0|<l> l|<r> r" ];
1446 h2 [ label = "h2\n0|<l> l|<r> r" ];
1447 h3 [ label = "h3\n2|<l> l|<r> r" ];
1448 h4 [ label = "h4\n0|<l> l|<r> r" ];
1449 h5 [ label = "h5\n1|<l> l|<r> r" ];
1450 h6 [ label = "h6\n1|<l> l|<r> r" ];
1452 root:r0 -> h1 [ style = invis ];
1453 h1:l -> h2 [ style = invis ];
1454 h1:r -> h3 [ style = invis ];
1456 h2:l -> h4 [ style = invis ];
1457 h2:r -> h5 [ style = invis ];
1459 h3:l -> h2 [ style = invis ];
1466 Luego se cambia una referencia (en vez de eliminarse) realizándose la
1467 operación ``update(h3.l, h5)``. Para esto primero se incrementa el contador
1468 de referencias de ``h5`` para evitar confundirlo accidentalmente con
1469 *basura* si se elimina alguna celda que apuntaba a ésta. Luego se procede
1470 a decrementar el contador de ``h2`` que queda en 0, transformándose en
1471 *basura* (ver figura :vref:`fig:gc-rc-up-1`). Lo mismo pasa cuando se
1472 desciende a ``h4``, pero al descender a ``h5`` y decrementar el contador,
1473 éste sigue siendo mayor que 0 (pues ``h3`` va a apuntar a ``h5``) así que
1474 permanece en el *live set*. Finalmente se termina de actualizar la
1475 referencia ``h3.l`` para que apunte a ``h5`` (ver figura
1476 :vref:`fig:gc-rc-up-2`).
1479 .. fig:: fig:gc-rc-cycle
1482 Eliminación de la referencia ``r1`` :math:`\to` ``h3`` (pérdida de
1483 memoria debido a un ciclo).
1487 El ejecutarse ``update(r1, null)`` se visita la celda ``h3``.
1494 edge [ color = gray40 ];
1504 subgraph cluster_all {
1507 label = "root\nset|<r0> r0|<r1> r1\n*",
1514 node [ fillcolor = white, fontcolor = black ];
1518 h1 [ label = "h1\n0|<l> l|<r> r" ];
1519 h1 [ label = "h1\n0|<l> l|<r> r" ];
1520 h2 [ label = "h2\n0|<l> l|<r> r" ];
1521 h3 [ label = "h3\n2|<l> l|<r> r" ];
1522 h4 [ label = "h4\n0|<l> l|<r> r" ];
1523 h5 [ label = "h5\n1|<l> l|<r> r" ];
1524 h6 [ label = "h6\n1|<l> l|<r> r" ];
1526 root:r0 -> h1 [ style = invis ];
1527 h1:l -> h2 [ style = invis ];
1528 h1:r -> h3 [ style = invis ];
1529 root:r1 -> h3 [ style = bold, color = black ];
1530 h2:l -> h4 [ style = invis ];
1531 h2:r -> h5 [ style = invis ];
1533 h3:l -> h2 [ style = invis ];
1541 Se decrementa el contador de ``h3`` pero sigue siendo mayor que 0 por
1549 edge [ color = gray40 ];
1559 subgraph cluster_all {
1562 label = "root\nset|<r0> r0|<r1> r1\n*",
1569 node [ fillcolor = white, fontcolor = black ];
1573 h1 [ label = "h1\n0|<l> l|<r> r" ];
1574 h1 [ label = "h1\n0|<l> l|<r> r" ];
1575 h2 [ label = "h2\n0|<l> l|<r> r" ];
1576 h3 [ label = "h3\n1|<l> l|<r> r" ];
1577 h4 [ label = "h4\n0|<l> l|<r> r" ];
1578 h5 [ label = "h5\n1|<l> l|<r> r" ];
1579 h6 [ label = "h6\n1|<l> l|<r> r" ];
1581 root:r0 -> h1 [ style = invis ];
1582 h1:l -> h2 [ style = invis ];
1583 h1:r -> h3 [ style = invis ];
1584 root:r1 -> h3 [ style = invis ];
1585 h2:l -> h4 [ style = invis ];
1586 h2:r -> h5 [ style = invis ];
1588 h3:l -> h2 [ style = invis ];
1595 Finalmente se presenta lo que sucede cuando se elimina la última referencia
1596 a un ciclo (en este caso un ciclo simple de 2 celdas: ``h3`` y ``h6``). Se
1597 elimina la única referencia externa al ciclo (``r1``), por lo que se visita
1598 la celda ``h3`` decrementando su contador de referencias, pero éste
1599 continúa siendo mayor que 0 porque la celda ``h6`` (parte del ciclo) la
1600 referencia. Por lo tanto el ciclo, y todas las celdas a las que apunta que
1601 no tienen otras referencias externas y por lo tanto deberían ser *basura*
1602 también (``h5``), no pueden ser recicladas y su memoria es perdida (ver
1603 figura :vref:`fig:gc-rc-cycle`).
1607 .. _ref_gc_mark_sweep:
1610 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1617 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1626 ----------------------------------------------------------------------------
1628 .. explicar la cantidad de cosas que hay (que son muchas) y dar algunos
1634 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1636 La cantidad de clasificaciones que pueden hacerse respecto a algoritmos de
1637 recolección de basura son muy grandes. Muchas de estas clasificaciones se
1638 superponen entre sí, habiendo algoritmos que aplican muchas técnicas a la
1639 vez o recolectores híbridos que aplican una técnica para algún tipo de
1640 objeto y otra para otros.
1642 A continuación se enumeran las clasificaciones más importantes que se
1643 pudieron observar en la investigación sobre el `estado del arte`_.
1650 Generalmente se llama recolección **directa** a aquella en la cual el
1651 compilador o lenguaje instrumenta al *mutator* de forma tal que la
1652 información de conectividad se mantenga activamente cada vez que hay un
1653 cambio en él. Normalmente se utiliza un contador de referencia en cada
1654 celda para este propósito, permitiendo almacenar en todo momento la
1655 cantidad de nodos que apuntan a ésta (dejando de lado el :ref:`problema de
1656 los ciclos <ref_gc_rc_cycles>`). Esto permite reclamar una celda
1657 instantáneamente cuando el *mutator* deja de hacer referencia a ella. Este
1658 tipo de recolectores son, inherentemente :ref:`incrementales <ref_gc_inc>`.
1660 Por el contrario, los recolectores **indirectos** normalmente no
1661 interfieren con el *mutator* en cada actualización del grafo de
1662 conectividad (exceptuando algunos :ref:`recolectores incrementales
1663 <ref_gc_inc>` que a veces necesitan instrumentar el *mutator* pero no para
1664 mantener el estado del grafo de conectividad completo). La recolección se
1665 dispara usualmente cuando el *mutator* requiere alocar memoria pero no hay
1666 más memoria libre conocida disponible y el recolector se encarga de generar
1667 la información de conectividad desde cero para determinar qué celdas son
1670 Estas son las dos grandes familias de recolectores, también conocidos como
1671 `conteo de referencias`_ (directa) y *traicing garbage collection*
1680 Recolección incremental es aquella que se realiza de forma intercalada con
1681 el *mutator*. En general el propósito es disminuir el tiempo de las pausas
1682 causadas por el recolector (aunque generalmente el resultado es un mayor
1683 costo en tiempo total de recolección).
1685 De los `algoritmos clásicos`_ el único que es incremental en su forma más
1686 básica es el `conteo de referencias`_. Otros recolectores pueden hacerse
1687 incrementales de diversas maneras, pero en general consta de hacer parte
1688 del trabajo de escanear el grafo de conectividad cada vez que el *mutator*
1689 aloca memoria. En general para hacer esto es también necesario instrumentar
1690 al *mutator* de forma tal que informe al recolector cada vez que cambia el
1691 grafo de conectividad, para que éste pueda marcar al sub-grafo afectado por
1692 el cambio como *desactualizado* y así re-escanearlo nuevamente en la
1695 En general la eficiencia de los recolectores incrementales disminuye
1696 considerablemente cuando el *mutator* actualiza muy seguido el grafo de
1697 conectividad, porque debe re-escanear sub-grafos que ya había escaneado una
1698 y otra vez. A esto se debe también que en general el tiempo de
1699 procesamiento total de un recolector incremental sea mayor que uno no
1700 incremental, aunque el tiempo de pausa de una recolección sea menor.
1703 .. _ref_gc_concurrent:
1705 Concurrente / *stop-the-world*
1706 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1708 Los recolectores concurrentes son aquellos que pueden correr en paralelo
1709 con el *mutator*. Por el contrario, aquellos que pausan el *mutator* para
1710 realizar la recolección son usualmente denominados *stop-the-world* (*parar
1711 el mundo*), haciendo referencia a que pausan todos los hilos del *mutator*
1712 para poder escanear el grafo de conectividad de forma consistente.
1714 Para lograr que un recolector sea concurrente generalmente el mecanismo es
1715 similar al necesario para hacer un :ref:`recolector incremental
1716 <ref_gc_inc>`: hay que instrumentar al *mutator* para que informe al
1717 recolector cuando se realiza algún cambio en el grafo de conectividad, de
1718 forma tal que pueda volver a escanear el sub-grafo afectado por el cambio.
1720 Esto también trae como consecuencia el incremento en el tiempo total que
1721 consume el recolector, debido a la necesidad de re-escanear sub-grafos que
1722 han sido modificados.
1728 1. Instrumentar el *mutator* de forma tal de que informe al recolector cada
1729 vez que cambia el grafo de conectividad, de manera que pueda re-escanear
1730 el sub-grafo que afectado por el cambio.
1732 2. Tomar una foto de la memoria y escanear esa foto estática en vez de la
1733 memoria original, de forma que cualquier cambio en del *mutator* no
1734 afecte la vista de la memoria del recolector.
1736 Ambos métodos tienen ventajas y desventajas. El primero no necesita hacer
1737 una copia costosa de la memoria, pero existe la posibilidad de que el
1738 recolector nunca pueda escanear el grafo de conectividad completo, si el
1739 *mutator* modifica el grafo más rápido de lo que el recolector lo escanea.
1740 Si bien hay formas de evitar esto, es usual que el trabajo de recolección
1741 se vea considerablemente incrementado por la cantidad de veces que hay que
1742 re-escanear el grafo. El segundo método puede ser costoso por las copias
1743 necesarias (aunque esto puede ser mitigado empleando alguna forma de *COW*
1744 [#gccow]_) y existe la posibilidad de que no se recuperen celdas que el
1748 .. [#gccow] *COW* (del inglés *Copy On Write* o *copiar al escribir*) es
1749 una técnica muy utilizada para disminuir las copias innecesarias,
1750 evitando hacer la copia hasta que haya una modificación. Mientras no
1751 hayan modificaciones no es necesario realizar una copia porque todos los
1752 interesados verán la misma información. Esta técnica es utilizada por el
1753 *kernel* de Linux_ por ejemplo, para que varias instancias de un mismo
1754 proceso puedan compartir la memoria.
1757 Boehm, H., and M. Weiser, "Garbage Collection in an Uncooperative
1758 Environment", Software Practice & Experience, September 1988, pp. 807-820.
1759 http://www.hpl.hp.com/personal/Hans_Boehm/spe_gc_paper/preprint.pdf
1760 (http://www.hpl.hp.com/personal/Hans_Boehm/spe_gc_paper/)
1762 Boehm, H., "Space Efficient Conservative Garbage Collection", Proceedings
1763 of the ACM SIGPLAN '93 Conference on Programming Language Design and
1764 Implementation, SIGPLAN Notices 28, 6 (June 1993), pp. 197-206.
1765 http://www.hpl.hp.com/personal/Hans_Boehm/gc/papers/pldi93.ps.Z
1767 Boehm, H., A. Demers, and S. Shenker, "Mostly Parallel Garbage Collection",
1768 Proceedings of the ACM SIGPLAN '91 Conference on Programming Language
1769 Design and Implementation, SIGPLAN Notices 26, 6 (June 1991), pp. 157-164.
1770 http://www.hpl.hp.com/personal/Hans_Boehm/gc/papers/pldi91.ps.Z
1772 Implementación de Bohem GC
1773 http://www.hpl.hp.com/personal/Hans_Boehm/gc/gcdescr.html
1775 Interfaz de Bohem GC
1776 http://www.hpl.hp.com/personal/Hans_Boehm/gc/gcinterface.html
1779 .. include:: links.rst
1781 .. vim: set ts=3 sts=3 sw=3 et tw=78 :