]> git.llucax.com Git - z.facultad/75.00/informe.git/blobdiff - source/gc.rst
Usar identación de 3 espacios y 78 columnas
[z.facultad/75.00/informe.git] / source / gc.rst
index 00cee40b8c282fa7b2a52ad1d8c4751cc8af115a..bf22c924f59d10aabeb2fd6737d7cf517766160e 100644 (file)
@@ -4,51 +4,79 @@
    un breve recorrido sobre el estado del arte.
    ESTADO: SIN EMPEZAR
 
-
 .. _ref_gc:
 
 Recolección de basura
 ============================================================================
 
-TODO
 
-.. Breve descripción de la utilidad de la recolección de basura
-   ESTADO: TERMINADO
-
-La recolección de basura es muchas veces vista por sus críticos de
-una forma bastante *naïve*. Muchas veces se argumenta que sólo es
-útil para programadores descuidados o que su necesidad es sólo una
-manifestación de un mal diseño. Si bien estas dos afirmaciones pueden
-ser, en algunos casos, ciertas, es falaz pensar que ese es la única
-ventaja de un recolector de basura. Uno de los aspectos más importantes
-de un recolector de basura es lograr un mayor nivel de abstracción
-[JOLI96]_. En particular, al diseñar o programar bibliotecas, de no
-haber un recolector de basura, **la administración de memoria pasa a ser
-parte de la interfaz de la biblioteca**. Y lo peor de este aspecto es
-que muy pocas veces esto es tenido en cuenta, derivando en bibliotecas
-muy difíciles de usar correctamente sin perder memoria, por no quedar
-bien clara la responsabilidad del manejo de memoria.
-
-Esto se debe a que, como se mencionó anteriormente, el manejo de memoria
-es un *artefacto* proveniente del *hardware*, no un concepto propio
-de los algoritmos a representar y como tal, nos impide desarrollar una
-mayor abstracción.
-
-Muchas veces se aduce también que la recolección de basura impide
-el desarrollo de programas eficientes. Si bien es innegable que la
-recolección de basura impone una carga extra, ésta es, en la mayoría
-de los casos, imperceptible. Incluso algunos algoritmos de recolección
-de basura pueden aumentar la eficiencia en casos determinados, como los
-recolectores que compactan, que pueden minimizar considerablemente la
-cantidad de páginas de memoria referenciadas por el programa, mejorando
-el *hit-ratio* tanto de la memoria virtual como del *cache*.  Aún si
-este no fuera el caso, o en casos de sistemas de tiempo real o zonas muy
-críticas en cuanto a la eficiencia, muchas veces es posible suspender
-el recolector de basura en dicho fragmento de código. Es cierto que esto
-rompe un poco con la idea de ganar abstracción, pero es necesario sólo
-en casos donde hay que realizar optimizaciones y las optimizaciones son,
-en general, dependientes de la plataforma (*hardware*) y por lo tanto
-de difícil abstracción.
+Introducción
+----------------------------------------------------------------------------
+
+*Recolección de basura* se refiere a la recuperación automática de memoria
+del *heap* [#gcheap]_ una vez que el programa ha dejado de hacer referencia
+a ella (y por lo tanto, ha dejado de utilizarla).
+
+.. [#gcheap] *Heap* es un área de memoria que se caracteriza por ser
+   dinámica (a diferencia del área de memoria estática que está disponible
+   durante toda la ejecución de un programa). Un programa puede reservar
+   memoria en tiempo de ejecución según sea necesario y liberarla cuando ya
+   no la necesita. A diferencia del *stack*, la duración de la *reserva* no
+   está atada a un bloque de código.
+
+A medida que el tiempo pasa, cada vez los programas son más complejos y es
+más compleja la administración de memoria. Uno de los aspectos más
+importantes de un recolector de basura es lograr un mayor nivel de
+abstracción y modularidad, dos conceptos claves en la ingeniería de
+software [JOLI96]_. En particular, al diseñar o programar bibliotecas, de
+no haber un recolector de basura, **la administración de memoria pasa a ser
+parte de la interfaz**, lo que produce que los módulos tengan un mayor
+grado de acoplamiento.
+
+Además hay una incontable cantidad de problemas asociados al manejo
+explícito de memoria que simplemente dejan de existir al utilizar un
+recolector de basura. Por ejemplo, los errores en el manejo de memoria
+(como *buffer overflows* [#gcbuff]_ o *dangling pointers* [#gcdang]_) son
+la causa más frecuente de problemas de seguridad [BEZO06]_.
+
+.. [#gcbuff] Un *buffer overflow* (*desbordamiento de memoria* en
+   castellano) se produce cuando se copia un dato a un área de memoria que
+   no es lo suficientemente grande para contenerlo. Esto puede producir que
+   el programa sea abortado por una violación de segmento, o peor,
+   sobreescribir un área de memoria válida, en cuyo caso los resultados son
+   impredecibles.
+
+.. [#gcdang] Un *dangling pointer* (*puntero colgante* en castellano) es un
+   puntero que apunta a un área de memoria inválida. Ya sea porque el
+   elemento apuntado no es el mismo tipo o porque la memoria ya ha sido
+   liberada. Al ser desreferenciado, los resultados son impredecibles, el
+   programa podría abortarse por una violación de segmento o podrían pasar
+   peores cosas si el área de memoria fue realocada para almacenar otro
+   objeto.
+
+La recolección de basura nació junto a Lisp_ a finales de 1950 y en los
+siguientes años estuvo asociada principalmente a lenguajes funcionales,
+pero en la actualidad está presente en prácticamente todos los lenguajes de
+programación, de alto o bajo nivel, aunque sea de forma opcional. En los
+últimos 10 años tuvo un gran avance, por la adopción en lenguajes de
+desarrollo rápido utilizados mucho en el sector empresarial, en especial
+Java_, que fue una plataforma de facto para la investigación y desarrollo
+de recolectores de basura (aunque no se limitaron a este lenguaje las
+investigaciones).
+
+En las primeras implementaciones de recolectores de basura la penalización
+en la eficiencia del programa se volvía prohibitiva para muchas
+aplicaciones. Es por esto que hubo bastante resistencia a la utilización de
+recolectores de basura, pero el avance en la investigación fue haciendo que
+cada vez sea una alternativa más viable al manejo manual de memoria,
+incluso para apliaciones con altos requerimientos de eficiencia. En la
+actualidad un programa que utiliza un recolector moderno puede ser
+comparable en eficiencia con uno que utiliza un esquema manual. En
+particular, si el programa fue diseñado con el recolector de basura en
+mente en ciertas circunstancias puede ser incluso más eficiente que uno que
+hace manejo explícito de la memoria. Muchos recolectores mejoran la
+localidad de referencia, haciendo que el programa tenga un mejor
+comportamiento con el caché y la memoria virtual.
 
 El recolector de basura debe tener un comportamiento correcto y predecible
 para que sea útil, si el programador no puede confiar en el recolector
@@ -57,83 +85,1364 @@ introduce nuevos puntos de falla en los programas, y lo que es peor,
 puntos de falla no controlados por el programador, volviendo mucho más
 difícil la búsqueda de errores.
 
-.. Presenta el problema y temas a ser tratados en el trabajo.
-   ESTADO: TERMINADO
-
-Los lenguajes de programación modernos tienen una tendencia cada vez
-más marcada a adoptar técnicas sofisticadas, haciéndolos más ricos y
-convirtiéndolos realmente en lenguajes, no en meros preprocesadores que
-convierten de una forma muy directa el código en *assembly*, permitiendo
-construcciones semánticamente más ricas y permitiendo al programar una
-mayor expresividad para plasmar algoritmos sin detenerse en los detalles
-del *hardware*.
-
-Estos conceptos supuestamente avanzados provienen, en general, de
-lenguajes académicos (muchos de ellos funcionales) que implementan estas
-funcionalidades hace mucho tiempo, pero que para su época, o bien no
-tuvieron suficiente difusión en el ambiente comercial, o bien eran muy
-lentos por la baja capacidad de procesamiento de la época o incluso
-demasiado *revolucionarios* para ser adoptados por programadores que no
-podían ver las ventajas que esos nuevos conceptos proveen.
-
-El caso de la recolección de basura (*garbage collection* en inglés)
-es uno de los más representativos. Lisp_ introdujo a principio de los
-'60 este concepto, como un mecanismo para alocar y liberar recursos
-(en general memoria alocada en el *heap*) de forma automática. Pero
-no fue hasta avanzados los '90 que esta técnica se empezó a utilizar
-en lenguajes de programación de uso comercial, cuando fue popularizado
-por Java_. Incluso luego de más de 30 años para Java_ era costosa la
-recolección de basura, lo que sumado a la presencia de una máquina
-virtual para ejecutar los programas producidos, condujo a que estos
-lenguajes sean notablemente lentos. Aún así Java_ creció y entre las
-mejoras introducidas hubieron mejoras en la recolección de basura. Otros
-lenguaje de programación populares que utilizan alguna forma de
-recolección de basura son Python_, Ruby_, PHP_ y `C#`_, entre otros.
 
 
 
-Introducción
-----------------------------------------------------------------------------
+Conceptos básicos
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-TODO
+Los programas pueden hacer uso principalmente de 4 áreas de memoria:
+
+Registros:
+   Se trata de la memoria más básica de una computadora. Es el área de
+   memoria en la que puede operar realmente el procesador, es extremadamente
+   escasa y generalmente su uso es administrado por el lenguaje de
+   programación (o compilador más específicamente). Excepto en situaciones
+   muy particulares, realizando tareas de muy bajo nivel, un programador
+   nunca manipula los registros explícitamente.
+
+Área de memoria estática:
+   Es la forma de memoria más simple que un programador utiliza
+   explícitamente. En general las variables globales se almacenan en este
+   área, que es parte inherente del programa y está disponible durante toda
+   su ejecución, por lo tanto nunca cambia su capacidad en tiempo de
+   ejecución. Es la forma más básica de administrar memoria, pero tiene una
+   limitación fundamental: **el tamaño de la memoria tiene que ser conocido
+   en tiempo de compilación**. Los primeros lenguajes de programación solo
+   contaban con este tipo de memoria (además de los registros del
+   procesador).
+
+*Stack* (pila):
+   Los primeros lenguajes de programación que hicieron uso de una pila
+   aparecieron en el año 1958 (Algol-58 y Atlas Autocode) y fueron los
+   primeros en introducir estructura de bloques, almacenando las
+   variables locales a estos bloques utilizando una pila [JOLI96]_.
+   Esto permite utilizar recursividad y tener un esquema simple de memoria
+   dinámica. Sin embargo este esquema es muy limitado porque el orden de
+   reserva y liberación de memoria tiene que estar bien establecido. Una
+   celda [#gccelda]_ alocada antes que otra nunca puede ser liberada antes
+   que aquella.
+
+   .. [#gccelda] En general en la literatura se nombra a una porción de
+      memoria alocada individualmente *celda*, *nodo* u *objeto*
+      indistintamente. En este trabajo se utilizará la misma nomenclatura
+      (haciendo mención explícita cuando alguno de estos términos se
+      refiera a otra cosa, como al nodo de una lista o a un objeto en el
+      sentido de programación orientada a objetos).
+
+*Heap*:
+   A diferencia del *stack*, el *heap* provee un área de memoria que puede
+   ser obtenida dinámicamente pero sin limitaciones de orden. Es el tipo de
+   memoria más flexible y por lo tanto el más complejo de administrar; razón
+   por la cual existen los recolectores de basura.
+
+La recolección de basura impone algunas restricciones sobre la manera de
+utilizar el *heap*. Debido a que un recolector de basura debe ser capaz de
+determinar el grafo de conectividad de la memoria en uso, es necesario que
+el programa siempre tenga alguna referencia a las celdas activas en los
+registros, memoria estática o *stack* (normalmente denominado *root set*).
+
+Esto implica que una celda sea considerada basura si y sólo si no puede ser
+alcanzada a través del grafo de conectividad que se comienza a recorrer
+desde el *root set*. Por lo tanto, una celda está *viva* si y sólo si su
+dirección de memoria está almacenada en una celda *raíz* (parte del *root
+set*) o si está almacenada en otra celda *viva* del *heap*.
+
+Expresado más formalmente, dada la relación :math:`M \to N`, donde
+:math:`M` es una celda del *heap* o parte del *root set* y :math:`N` es una
+celda del *heap*, definida como:
+
+.. math::
+
+   M \to N \Longleftrightarrow M \text{ almacena un puntero a } N
+
+El conjunto de celdas vivas (o *live set*) queda determinado por:
+
+.. math::
+
+   vivas = \left\lbrace N \in Celdas \big/
+      ( \exists r \in Raices / r \to N ) \vee (\exists M \in vivas / M \to N )
+   \right\rbrace
+
+Cabe aclarar que esta es una definición conceptual, asumiendo que el
+programa siempre limpia una dirección de memoria almacenada en el *root
+set* o una celda del *heap* cuando la celda a la que apunta no va a ser
+utilizada nuevamente. Esto no es siempre cierto y los falsos positivos que
+esto produce se conoce como un tipo de pérdida de memoria (que es posible
+incluso al utilizar un recolector de basura) llamada pérdida de memoria
+*lógica*. Esto puede no ser evitable (incluso cuando el programador no
+cometa errores) en lenguajes de programación que requieran un recolector de
+basura conservativo.
+
+Por último, siendo que el recolector de basura es parte del programa de
+forma indirecta, es común ver en la literatura que se direfencia entre
+2 partes del programa, el recolector de basura y el programa en sí. Dado
+que para el recolector de basura, lo único que interesa conocer del
+programa en sí son los cambios al grafo de conectividad de las celdas,
+normalmente se lo llama *mutator* (mutador).
+
+
+
+
+Recorrido del grafo de conectividad
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+El problema de encontrar las celdas *vivas* de un programa se reduce a recorrer
+un grafo dirigido. El grafo se define como:
+
+.. math::
+
+   G = (V,A)
+
+Donde :math:`V` es el conjunto de vértices, dado por las celdas de memoria
+y :math:`A` es un conjunto de pares ordenados (aristas), dado por la
+relación :math:`M \rightarrow N` (es decir, los punteros).
+
+El grafo comienza a recorrerse desde el *root set* y todos los vértices que
+fueron visitados componen el *live set*; el resto de los vértices son
+*basura*.
+
+Más formalmente, Definimos:
+
+*Camino*:
+   secuencia de vértices tal que cada uno de los vértices tiene una arista
+   al próximo vértice en la secuencia. Todo camino finito tiene un *vértice
+   inicial* y un *vértice final* (llamados en conjunto *vértices
+   terminales*). Cualquier vértice no terminal es denominado *vértice
+   interior*.
+
+   .. math::
+
+      \underset{v_1 \rightarrow v_N}{C} = \left\lbrace
+         v_1, \dotsc, v_N \in V \big/ \underset{i \in [1,N-1]}{\forall v_i}
+            \exists (v_i \to v_{i+1}) \in A
+      \right\rbrace
+
+*Conexión*:
+   decimos que :math:`M` está *conectado* a :math:`N` si y sólo si existe un
+   camino de :math:`M` a :math:`N`.
+
+   .. math::
+
+      M \mapsto N \Longleftrightarrow \exists \underset{M \to N}{C} \in G
+
+*Live set*:
+   el conjunto de celdas *vivas* está dado por todos los vértices
+   (:math:`v`) del grafo para los cuales existe una raíz en el *root set*
+   que esté conectada a él.
+
+   .. math::
+
+      Live \thickspace set = \left\lbrace v \in V \big/
+         \left( \exists r \in Root \thickspace set \big/ r \mapsto v \right)
+      \right\rbrace
+
+*Basura*:
+   la basura, o celdas *muertas*, quedan determinadas entonces por todas las
+   celdas del *heap* que no son parte del *live set*.
+
+   .. math::
+
+      Basura = V - Live \thickspace set
+
+Esto es, efectivamente, una partición del *heap* (ver figura
+:vref:`fig:gc-heap-parts`).
+
+
+.. fig:: fig:gc-heap-parts
+
+   Distintas partes de la memoria, incluyendo relación entre *basura*,
+   *live set*, *heap* y *root set*.
+
+   .. digraph:: g1
+
+      margin  = 0;
+      ratio   = fill;
+      size    = "4.6,3.6";
+      node [ shape = record, width = 0, height = 0 ];
+
+      subgraph cluster_heap {
+         label = "Heap";
+         style     = dashed;
+         color     = gray40;
+         fontcolor = gray40;
+
+         subgraph cluster_live {
+            label     = "Live set";
+            style     = dashed;
+            color     = gray40;
+            fontcolor = gray40;
+            node [
+               style     = filled,
+               fillcolor = gray25,
+               fontcolor = white,
+            ];
+            h1; h2; h4; h5; h6;
+         }
+
+         subgraph cluster_garbage {
+            label     = "Basura";
+            style     = dashed;
+            color     = gray40;
+            fontcolor = gray40;
+            node [ style = filled, fillcolor = white ];
+            h7; h3;
+         }
+      }
+
+      subgraph cluster_root {
+         label     = "Root set";
+         style     = dashed;
+         color     = gray40;
+         fontcolor = gray40;
+         node [ style = filled, fillcolor = gray96 ];
+         r0; r1; r2;
+      }
+
+      r0 -> h1 -> h2 -> h5;
+      r1 -> h5 -> h6 -> h1;
+      r2 -> h4;
+      h5 -> h4;
+      h7 -> h1;
+      h7 -> h3;
+
+
+Al proceso de visitar los vértices *conectados* desde el *root set* se lo
+denomina *marcado*, *fase de marcado* o *mark phase* en inglés, debido
+a que es necesario marcar los vértices para evitar visitar 2 veces el mismo
+nodo en casos de que el grafo contenga ciclos [#gccycle]_. De forma similar
+a la búsqueda, que puede realizarse *primero a lo ancho* (*breadth-first*)
+o *primero a lo alto* (*depth-first*) del grafo, el marcado de un grafo
+también puede realizarse de ambas maneras. Cada una podrá o no tener
+efectos en la eficiencia, en particular dependiendo de la aplicación puede
+convenir uno u otro método para lograr una mejor localidad de referencia
+y de esta manera tener un mejor comportamiento de la memoria virtual o del
+*caché*.
+
+.. [#gccycle] Un ciclo es un camino donde el *vértice inicial* es el mismo
+   que el *vértice final*. Por lo tanto, los *vértices terminales* son
+   completamente arbitrarios, ya que cualquier *vértice interior* puede ser
+   un *vértice terminal*.
+
+Un algoritmo simple (recursivo) de marcado *primero a lo alto*  puede ser
+el siguiente (asumiendo que partimos con todos los vértices sin marcar)
+[#gcpseudo]_::
+
+   mark(v)
+      if not v.marked
+         v.marked = true
+         for (src, dst) in v.edges
+            mark(dst)
+
+   mark_phase()
+      for r in root_set
+         mark(r)
+
+.. [#gcpseudo] Para presentar los algoritmos se utiliza una forma simple de
+   pseudo-código. El pseudo-código se escribe en inglés para que pueda ser
+   más fácilmente contrastado con la literatura, que está en inglés. Para
+   diferenciar posiciones de memoria y punteros de las celdas en sí, se usa
+   la misma sintaxis que C, ``r*`` denota una referencia o puntero y ``*r``
+   denota "objeto al que apunta ``r``\ ". Se sobreentiende que ``r = o``
+   siempre toma la dirección de memoria de ``o``.
+
+Una vez concluido el marcado, sabemos que todos los vértices con la marca
+son parte del *live set* y que todos los vértices no marcados son *basura*.
+Esto es conocido también como **abstracción bicolor**, dado que en la
+literatura se habla muchas veces de *colorear* las celdas. En general, una
+celda sin marcar es de color blanco y una marcada de color negro.
+
+Puede observarse un ejemplo del algoritmo en la figura
+:vref:`fig:gc-mark-1`, en la cual se marca el sub-grafo apuntando por
+``r0``. Luego se marca el sub-grafo al que apunta ``r1`` (figura
+:vref:`fig:gc-mark-2`), concluyendo con el marcado del grafo completo,
+dejando sin marcar solamente las celdas *basura* (en blanco).
+
+
+.. fig:: fig:gc-mark-1
+
+   Ejemplo de marcado del grafo de conectividad (parte 1).
+
+   .. subfig::
+
+      Se comienza a marcar el grafo por la raíz r0.
+
+      .. digraph:: g2_1
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         node [ shape = record, width = 0, height = 0];
+         edge [ color = gray40 ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0\n*|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+            ];
+
+            subgraph marked {
+               node [ style = filled, fillcolor = gray25, fontcolor = white ];
+               h1;
+            };
+
+            root:r0 -> h1 [ style = bold, color = black ];
+            h1 -> h2 -> h5 -> h1;
+            root:r1 -> h6 -> h2;
+            h4 -> h3;
+            h3 -> h5;
+
+         }
+
+   .. subfig::
 
+      Luego de marcar el nodo ``h1``, se procede al ``h2``.
 
+      .. digraph:: g2_2
 
-Algoritmos principales
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         node [ shape = record, width = 0, height = 0 ];
+         edge [ color = gray40 ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0\n*|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+            ];
+
+            subgraph marked {
+               node [ style = filled, fillcolor = gray25, fontcolor = white ];
+               h1; h2;
+            };
+
+            root:r0 -> h1 [ color = gray10 ];
+            h1 -> h2 [ style = bold, color = black ];
+            h2 -> h5 -> h1;
+            root:r1 -> h6 -> h2;
+            h4 -> h3;
+            h3 -> h5;
+
+         }
+
+   .. subfig::
+
+      Luego sigue el nodo h5.
+
+      .. digraph:: g2_3
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         node [ shape = record, width = 0, height = 0 ];
+         edge [ color = gray40 ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0\n*|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+            ];
+
+            subgraph marked {
+               node [ style = filled, fillcolor = gray25, fontcolor = white ];
+               h1; h2; h5;
+            };
+
+            root:r0 -> h1 [ color = gray10 ];
+            h1 -> h2 [ color = gray10 ];
+            h2 -> h5 [ style = bold, color = black ];
+            h5 -> h1;
+            root:r1 -> h6 -> h2;
+            h4 -> h3;
+            h3 -> h5;
+
+         }
+
+
+.. fig:: fig:gc-mark-2
+
+   Ejemplo de marcado del grafo de conectividad (parte 2).
+
+   .. subfig::
+
+      El nodo h5 tiene una arista al h1, pero el h1 ya fue visitado, por lo
+      tanto no se visita nuevamente.
+
+      .. digraph:: g2_4
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         node [ shape = record, width = 0, height = 0 ];
+         edge [ color = gray40 ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0\n*|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+            ];
+
+            subgraph marked {
+               node [ style = filled, fillcolor = gray25, fontcolor = white ];
+               h1; h2; h5;
+            };
+
+            root:r0 -> h1 [ color = gray10 ];
+            h1 -> h2 [ color = gray10 ];
+            h2 -> h5 [ color = gray10 ];
+            h5 -> h1 [ style = bold, color = black ];
+            root:r1 -> h6 -> h2;
+            h4 -> h3;
+            h3 -> h5;
+
+         }
+
+   .. subfig::
+
+      Se concluye el marcado del sub-grafo al que conecta r0, se procede
+      a marcar el sub-grafo al que conecta r1, marcando al nodo h6.
+
+      .. digraph:: g2_5
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         node [ shape = record, width = 0, height = 0 ];
+         edge [ color = gray40 ];
+
+         subgraph cluster_all {
+
+           root [
+              label     = "root\nset|<r0> r0|<r1> r1\n*",
+              style     = filled,
+              fillcolor = gray96,
+           ];
+
+           subgraph marked {
+              node [ style = filled, fillcolor = gray25, fontcolor = white ];
+              h1; h2; h5; h6;
+           };
+
+           root:r0 -> h1 [ color = gray10 ];
+           h1 -> h2 [ color = gray10 ];
+           h2 -> h5 [ color = gray10 ];
+           h5 -> h1 [ color = gray10 ];
+           root:r1 -> h6 [ style = bold, color = black ];
+           h6 -> h2;
+           h4 -> h3;
+           h3 -> h5;
+
+         }
+
+   .. subfig::
+
+      El nodo h6 tiene una arista al h2, pero éste ya fue marcado por lo
+      que no se vuelve a visitar. No hay más raíces, se finaliza el marcado
+      del grafo.
+
+      .. digraph:: g2_6
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         node [ shape = record, width = 0, height = 0 ];
+         edge [ color = gray40 ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0|<r1> r1\n*",
+               style     = filled,
+               fillcolor = gray96,
+            ];
+
+            subgraph marked {
+               node [ style = filled, fillcolor = gray25, fontcolor = white ];
+               h1; h2; h5; h6;
+            };
+
+            root:r0 -> h1 [ color = gray10 ];
+            h1 -> h2 [ color = gray10 ];
+            h2 -> h5 [ color = gray10 ];
+            h5 -> h1 [ color = gray10 ];
+            root:r1 -> h6 [ color = gray10 ];
+            h6 -> h2 [ style = bold, color = black ];
+            h4 -> h3;
+            h3 -> h5;
+
+         }
+
+
+
+.. _ref_gc_tricolor:
+
+Abstracción tricolor
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Muchos algoritmos utilizan tres colores para realizar el marcado. El tercer
+color, gris generalmente, indica que una celda debe ser visitada. Esto
+permite algoritmos :ref:`concurrentes <ref_gc_concurrent>`
+e :ref:`incrementales <ref_gc_inc>`, además de otro tipo de optimizaciones.
+Entonces, lo que plantea esta abtracción es una nueva partición del heap al
+momento de marcar, esta vez son 3 porciones: blanca, gris y negra.
+
+Al principio todas las celdas se pintan de blanco, excepto el *root set*
+que se punta de gris. Luego se van obteniendo celdas del conjunto de las
+grises y se las pinta de negro, pintando sus hijas directas de gris.
+
+Una vez que no hay más celdas grises, tenemos la garantía de que las celdas
+negras serán el *live set* y las celdas blancas *basura*. Esto se debe
+a que siempre se mantiene esta invariante: **ninguna celda negra apunta
+directamente a una celda blanca**. Las celdas blancas siempre son apuntadas
+por celdas blancas o grises. Entonces, siempre que el conjunto de celdas
+grises sea vacío, no habrán celdas negras conectadas a blancas, siendo las
+celdas blancas *basura*.
+
+El algoritmo básico para marcar con tres colores es el siguiente (asumiendo
+que todas las celdas parten pintadas de blanco, es decir, el conjunto
+blanco contiene todas las celdas de memoria y los conjuntos negro y gris
+están vacíos)::
+
+   mark_phase()
+      for r in root_set
+         gray_set.add(r)
+      while not gray_set.empty()
+         v = gray_set.pop()
+         black_set.add(v)
+         for (src, dst) in v.edges
+            if v in white_set
+               white_set.remove(v)
+               gray_set.add(v)
+
+Es simple notar que este algoritmo es naturalmente no recursivo, lo que de
+por sí ya presenta una ventaja sobre el marcado *bicolor*.
+
+
+
+.. _ref_gc_services:
+
+Servicios
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+En general todos los algoritmos de recolección de basura utilizan servicios
+de una capa inferior [#gclowlayer]_ y proveen servicios a una capa superior
+[#gchilayer]_.
+
+.. [#gclowlayer] En general estos servicios están provistos directamente
+   por el sistema operativo pero también pueden estar dados por un
+   administrador de memoria de bajo nivel (o *low level allocator* en
+   inglés).
+
+.. [#gchilayer] En general estos servicios son utilizados directamente por
+   el lenguaje de programación, pero pueden ser utilizados directamente por
+   el usuario del lenguaje si éste interatúa con el recolector, ya sea por
+   algún requerimiento particular o porque el lenguaje no tiene soporte
+   diercto de recolección de basura y el recolector está implementado como
+   una biblioteca de usuario.
+
+A continuación se presentan las primitivas en común que utilizan todos los
+recolectores a lo largo de este documento.
+
+Servicios utilizados por el recolector son los siguientes:
+
+:math:`alloc() \to cell`:
+   obtiene una nueva celda de memoria. El mecanismo por el cual se obtiene
+   la celda es indistinto para esta sección, puede ser de una lista libre,
+   puede ser de un administrador de memoria de más bajo nivel provisto por
+   el sistema operativo o la biblioteca estándar de C (``malloc()``), etc.
+   Cómo organizar la memoria es un área de investigación completa y si bien
+   está estrechamente relacionada con la recolección de basura, en este
+   trabajo no se prestará particular atención a este aspecto (salvo casos
+   donde el recolector impone una cierta organización de memoria en el *low
+   level allocator*). Por simplicidad también asumiremos (a menos que se
+   indique lo contrario) que las celdas son de tamaño fijo. Esta restricción
+   normalmente puede ser fácilmente relajada (en los recolectores que la
+   tienen).
+
+:math:`free(cell)`:
+   libera una celda que ya no va a ser utilizada. La celda liberada debe
+   haber sido obtenida mediante ``alloc()``.
+
+Y los servicios básicos proporcionados por el recolector son los
+siguientes:
+
+:math:`new() \to cell`:
+   obtiene una celda de memoria para ser utilizada por el programa.
+
+:math:`update(ref, cell)`:
+   notifica al recolector que la referencia :math:`ref` ahora apunta
+   a :math:`cell`. Visto más formalmente, sería análogo a decir que hubo un
+   cambio en la conectividad del grafo: la arista :math:`src \to old` cambia
+   por :math:`src \to new` (donde :math:`src` es la celda que contiene la
+   referencia :math:`ref`, :math:`old` es la celda a la que apunta la
+   referencia :math:`ref` y :math:`new` es el argumento :math:`cell`). Si
+   :math:`cell` es ``null``, sería análogo a informar que se elimina la
+   arista :math:`src \to old`.
+
+:math:`del(cell)`:
+   este servicio, según el algoritmo, puede ser utilizado para informar un
+   cambio en la conectividad del grafo, la eliminación de una arista
+   (análogo a :math:`update(ref, null)` pero sin proporcionar información
+   sobre la arista a eliminar). Esto es generalmente útil solo en
+   :ref:`conteo de referencias <ref_gc_rc>`. Para otros recolectores puede
+   significar que el usuario asegura que no hay más referencias a esta
+   celda, es decir, análogo a eliminar el conjunto de aristas
+   :math:`\big\lbrace (v, w) \in A , v \in Live \thickspace set , w \in
+   Live \thickspace set \big/ w = cell`.
+
+:math:`collect()`:
+   indica al recolector que debe hacer un análisis del grafo de conectividad
+   en busca de *basura*. Generalmente este servicio es invocado por el
+   propio recolector cuando no hay más celdas reciclables.
+
+No todos los servicios son implementados por todos los recolectores, pero
+son lo suficientemente comunes como para describirlos de forma general en
+esta sección. Algunos son principalmente ideados para uso interno del
+recolector, aunque en ciertas circunstancias pueden ser utilizados por el
+usuario también.
+
+
+
+
+.. _ref_gc_clasic:
+
+Algoritmos clásicos
 ----------------------------------------------------------------------------
 
-TODO
+En la literatura se encuentran normalmente referencias a 3 algoritmos
+clásicos, que son utilizados generalmente como bloques básicos para
+construir recolectores más complejos. Se presentan las versiones históricas
+más simples a fin de facilitar la comprensión conceptual.
 
 
 
+.. _ref_gc_rc:
+
 Conteo de referencias
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-TODO
+Se trata del algoritmo más antiguo de todos, implementado por primera vez
+por `John McCarthy`_ para Lisp_ a finales de 1950. Se trata de un método
+:ref:`directo <ref_gc_direct>` e :ref:`incremental <ref_gc_inc>` por
+naturaleza, ya que distribuye la carga de la recolección de basura durante
+toda la ejecución del programa, cada vez que el *mutator* cambia la
+conectividad de algún nodo del grafo de conectividad.
 
+El método consiste en tener un contador asociado a cada celda que contenga
+la cantidad de celdas **vivas** que apuntan a ésta. Es decir, es la
+cardinalidad del conjunto de aristas que tienen por destino a la celda.
+Formalmente podemos definir el contador :math:`rc(v)` (de *reference
+counter* en inglés) de la siguiente manera:
 
+.. math::
 
-Marcado y barrido
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   rc(v) = \big\lvert
+      \left\lbrace
+         (v_1, v_2) \in A \big/
+            v_1 \in Live \thickspace set \cup Root \thickspace set
+               \wedge v_2 = v
+      \right\rbrace
+   \big\rvert
 
-TODO
+El *mutator* entonces debe actualizar este contador cada vez que el grafo
+de conectividad cambia, es decir, cada vez que se agrega, modifica
+o elimina una arista del grafo (o visto de una forma más cercana al código,
+cada vez que se agrega, modifica o elimina un puntero).
 
+Esta invariante es fundamental para el conteo de referencias, porque se
+asume que si el contador es 0 entonces el *mutator* no tiene ninguna
+referencia a la celda y por lo tanto es *basura*:
 
+.. math::
 
-Copia/Semi-espacio
+   rc(v) = 0 \Rightarrow v \in Basura
+
+Para mantener esta invariante el *mutator*, cada vez que cambia un puntero
+debe decrementar en 1 el contador de la celda a la que apuntaba
+antiguamente  e incrementar en 1 el contador de la celda a la que apunta
+luego de la modificación. Esto asegura que la invariante se mantenga
+durante toda la ejecución del programa. Si al momento de decrementar un
+contador éste queda en 0, la celda asociada debe liberarse de forma de
+poder ser reciclada. Esto implica que si esta celda almacena punteros, los
+contadores de las celdas apuntadas deben ser decrementados también, porque
+solo deben almacenarse en el contador las aristas del *live set* para
+mantener la invariante. De esto puede resultar que otros contadores de
+referencia queden en 0 y más celdas sean liberadas. Por lo tanto,
+teóricamente la complejidad de eliminar una referencia puede ser
+:math:`O(\lvert Live \thickspace set \rvert)` en el peor caso.
+
+Las primitivas implementadas para este tipo de recolector son las
+siguientes (acompañadas de una implementación básica)::
+
+   new()
+      cell = alloc()
+      if cell is null
+         throw out_of_memory
+      cell.rc = 1
+      return cell
+
+   del(cell)
+      cell.rc = cell.rc - 1
+      if cell.rc is 0
+         for child* in cell.children
+            del(*child)
+         free(cell)
+
+   update(ref*, cell)
+      cell.rc = cell.rc + 1
+      del(*ref)
+      *ref = cell
+
+
+.. _ref_gc_rc_cycles:
+
+Ciclos
+^^^^^^
+
+.. _ref_gc_rc_example:
+
+Ejemplo
+^^^^^^^
+
+A continuación se presenta un ejemplo gráfico para facilitar la comprensión
+del algoritmo. Por simplicidad se asumen celdas de tamaño fijo con dos
+punteros, ``left`` (``l``) y ``right`` (``r``) y se muestra el contador de
+referencias abajo del nombre de cada celda. Se parte con una pequeña
+estructura ya construída y se muestra como opera el algoritmo al eliminar
+o cambiar una referencia (cambios en la conectividad del grafo). En un
+comienzo todas las celdas son accesibles desde el *root set* por lo tanto
+son todas parte del *live set*.
+
+Se comienza por eliminar la referencia de ``r0`` a ``h1``, que determina
+que ``h1`` se convirtió en *basura* (figura :vref:`fig:gc-rc-rm-1`). Esto
+conduce al decremento del contador de ``h2`` y ``h3`` que permanecen en el
+*live set* ya que sus contadores siguen siendo mayores a 0 (figura
+:vref:`fig:gc-rc-rm-2`).
+
+
+.. fig:: fig:gc-rc-rm-1
+
+   Eliminación de la referencia ``r0`` :math:`\to` ``h1`` (parte 1).
+
+   .. subfig::
+
+      Estado inicial del grafo de conectividad.
+
+      .. digraph:: g3_1
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         edge [ color = gray40 ];
+         node [
+            shape     = record,
+            width     = 0,
+            height    = 0,
+            style     = filled,
+            fillcolor = gray25,
+            fontcolor = white
+         ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+               fontcolor = black,
+            ];
+
+            h1 [ label = "h1\n1|<l> l|<r> r" ];
+            h2 [ label = "h2\n2|<l> l|<r> r" ];
+            h3 [ label = "h3\n2|<l> l|<r> r" ];
+            h4 [ label = "h4\n1|<l> l|<r> r" ];
+            h5 [ label = "h5\n1|<l> l|<r> r" ];
+            h6 [ label = "h6\n1|<l> l|<r> r" ];
+
+            root:r0 -> h1;
+            root:r1 -> h3;
+            h1:l -> h2;
+            h1:r -> h3;
+            h2:l -> h4;
+            h2:r -> h5;
+            h3:l -> h2;
+            h3:r -> h6;
+
+         }
+
+   .. subfig::
+
+      Al ejecutarse ``update(r0, null)``, se comienza por visitar la celda
+      ``h1``.
+
+      .. digraph:: g3_2
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         edge [ color = gray40 ];
+         node [
+            shape     = record,
+            width     = 0,
+            height    = 0,
+            style     = filled,
+            fillcolor = gray25,
+            fontcolor = white
+         ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0\n*|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+               fontcolor = black,
+            ];
+
+            h1 [ label = "h1\n1|<l> l|<r> r" ];
+            h2 [ label = "h2\n2|<l> l|<r> r" ];
+            h3 [ label = "h3\n2|<l> l|<r> r" ];
+            h4 [ label = "h4\n1|<l> l|<r> r" ];
+            h5 [ label = "h5\n1|<l> l|<r> r" ];
+            h6 [ label = "h6\n1|<l> l|<r> r" ];
+
+            root:r0 -> h1 [ style = bold, color = black ];
+            root:r1 -> h3;
+            h1:l -> h2;
+            h1:r -> h3;
+            h2:l -> h4;
+            h2:r -> h5;
+            h3:l -> h2;
+            h3:r -> h6;
+
+         }
+
+   .. subfig::
+
+      Se decrementa el contador de ``h1`` quedando en 0 (pasa a ser
+      *basura*). Se elimina primero ``h1.l`` y luego ``h1.r``.
+
+      .. digraph:: g3_3
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         edge [ color = gray40 ];
+         node [
+            shape     = record,
+            width     = 0,
+            height    = 0,
+            style     = filled,
+            fillcolor = gray25,
+            fontcolor = white
+         ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0\n*|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+               fontcolor = black,
+            ];
+
+            subgraph marked {
+               node [ fillcolor = white, fontcolor = black ];
+               h1;
+            };
+
+            h1 [ label = "h1\n0|<l> l|<r> r" ];
+            h2 [ label = "h2\n2|<l> l|<r> r" ];
+            h3 [ label = "h3\n2|<l> l|<r> r" ];
+            h4 [ label = "h4\n1|<l> l|<r> r" ];
+            h5 [ label = "h5\n1|<l> l|<r> r" ];
+            h6 [ label = "h6\n1|<l> l|<r> r" ];
+
+            root:r0 -> h1 [ style = invis ];
+            root:r1 -> h3;
+            h1:l -> h2 [ style = bold, color = black ];
+            h1:r -> h3;
+            h2:l -> h4;
+            h2:r -> h5;
+            h3:l -> h2;
+            h3:r -> h6;
+
+         }
+
+
+.. fig:: fig:gc-rc-rm-2
+
+   Eliminación de la referencia ``r0`` :math:`\to` ``h1`` (parte 2).
+
+   .. subfig::
+
+      Se decrementa el contador de ``h2`` pero no queda en 0 (permanece en
+      el *live set*).
+
+      .. digraph:: g3_4
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         edge [ color = gray40 ];
+         node [
+            shape     = record,
+            width     = 0,
+            height    = 0,
+            style     = filled,
+            fillcolor = gray25,
+            fontcolor = white
+         ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0\n*|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+               fontcolor = black,
+            ];
+
+            subgraph marked {
+               node [ fillcolor = white, fontcolor = black ];
+               h1;
+            };
+
+            h1 [ label = "h1\n0|<l> l|<r> r" ];
+            h2 [ label = "h2\n1|<l> l|<r> r" ];
+            h3 [ label = "h3\n2|<l> l|<r> r" ];
+            h4 [ label = "h4\n1|<l> l|<r> r" ];
+            h5 [ label = "h5\n1|<l> l|<r> r" ];
+            h6 [ label = "h6\n1|<l> l|<r> r" ];
+
+            root:r0 -> h1 [ style = invis ];
+            root:r1 -> h3;
+            h1:l -> h2 [ style = invis ];
+            h1:r -> h3 [ style = bold, color = black ];
+            h2:l -> h4;
+            h2:r -> h5;
+            h3:l -> h2;
+            h3:r -> h6;
+
+         }
+
+   .. subfig::
+
+      El contador de ``h3`` tampoco queda en 0, sigue en el *live set*.
+
+      .. digraph:: g3_5
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         edge [ color = gray40 ];
+         node [
+            shape     = record,
+            width     = 0,
+            height    = 0,
+            style     = filled,
+            fillcolor = gray25,
+            fontcolor = white
+         ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+               fontcolor = black,
+            ];
+
+            subgraph marked {
+               node [ fillcolor = white, fontcolor = black ];
+               h1;
+            };
+
+            h1 [ label = "h1\n0|<l> l|<r> r" ];
+            h2 [ label = "h2\n1|<l> l|<r> r" ];
+            h3 [ label = "h3\n1|<l> l|<r> r" ];
+            h4 [ label = "h4\n1|<l> l|<r> r" ];
+            h5 [ label = "h5\n1|<l> l|<r> r" ];
+            h6 [ label = "h6\n1|<l> l|<r> r" ];
+
+            root:r0 -> h1 [ style = invis ];
+            root:r1 -> h3;
+            h1:l -> h2 [ style = invis ];
+            h1:r -> h3 [ style = invis ];
+            h2:l -> h4;
+            h2:r -> h5;
+            h3:l -> h2;
+            h3:r -> h6;
+
+         }
+
+
+Luego se cambia una referencia (en vez de eliminarse) realizándose la
+operación ``update(h3.l, h5)``. Para esto primero se incrementa el contador
+de referencias de ``h5`` para evitar confundirlo accidentalmente con
+*basura* si se elimina alguna celda que apuntaba a ésta. Luego se procede
+a decrementar el contador de ``h2`` que queda en 0, transformándose en
+*basura* (figura :vref:`fig:gc-rc-up-1`). Lo mismo pasa cuando se desciende
+a ``h4``, pero al descender a ``h5`` y decrementar el contador, éste sigue
+siendo mayor que 0 (pues ``h3`` va a apuntar a ``h5``) así que permanece en
+el *live set*. Finalmente se termina de actualizar la referencia ``h3.l``
+para que apunte a ``h5`` (figura :vref:`fig:gc-rc-up-3`).
+
+
+.. fig:: fig:gc-rc-up-1
+
+   Cambio en la referencia ``h2.l`` :math:`\to` ``h2`` a ``h2.l``
+   :math:`\to` ``h5`` (parte 1).
+
+   .. subfig::
+
+      Comienza ``update(h3.l, h5)``, se incrementa el contador de ``h5``.
+
+      .. digraph:: g4_1
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         edge [ color = gray40 ];
+         node [
+            shape     = record,
+            width     = 0,
+            height    = 0,
+            style     = filled,
+            fillcolor = gray25,
+            fontcolor = white
+         ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+               fontcolor = black,
+            ];
+
+            subgraph marked {
+               node [ fillcolor = white, fontcolor = black ];
+               h1;
+            };
+
+            h1 [ label = "h1\n0|<l> l|<r> r" ];
+            h2 [ label = "h2\n1|<l> l|<r> r" ];
+            h3 [ label = "h3\n1|<l> l\n*|<r> r" ];
+            h4 [ label = "h4\n1|<l> l|<r> r" ];
+            h5 [ label = "h5\n2|<l> l|<r> r" ];
+            h6 [ label = "h6\n1|<l> l|<r> r" ];
+
+            root:r0 -> h1 [ style = invis ];
+            h1:l -> h2 [ style = invis ];
+            h1:r -> h3 [ style = invis ];
+            root:r1 -> h3;
+            h2:l -> h4;
+            h2:r -> h5;
+            h3:l -> h2;
+            h3:l -> h5 [ style = dotted, color = black ];
+            h3:r -> h6;
+
+         }
+
+   .. subfig::
+
+      Luego se procede a visitar las hijas de ``h3``, comenzando por ``h2``.
+
+      .. digraph:: g4_2
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         edge [ color = gray40 ];
+         node [
+            shape     = record,
+            width     = 0,
+            height    = 0,
+            style     = filled,
+            fillcolor = gray25,
+            fontcolor = white
+         ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+               fontcolor = black,
+            ];
+
+            subgraph marked {
+               node [ fillcolor = white, fontcolor = black ];
+               h1;
+            };
+
+            h1 [ label = "h1\n0|<l> l|<r> r" ];
+            h2 [ label = "h2\n1|<l> l|<r> r" ];
+            h3 [ label = "h3\n1|<l> l\n*|<r> r" ];
+            h4 [ label = "h4\n1|<l> l|<r> r" ];
+            h5 [ label = "h5\n2|<l> l|<r> r" ];
+            h6 [ label = "h6\n1|<l> l|<r> r" ];
+
+            root:r0 -> h1 [ style = invis ];
+            h1:l -> h2 [ style = invis ];
+            h1:r -> h3 [ style = invis ];
+            root:r1 -> h3;
+            h2:l -> h4;
+            h2:r -> h5;
+            h3:l -> h2 [ style = bold, color = black ];
+            h3:l -> h5 [ style = dotted, color = black ];
+            h3:r -> h6;
+
+         }
+
+   .. subfig::
+
+      Se decrementa el contador de ``h2`` y queda en 0 (pasa a ser
+      *basura*). Se eliminan las referencias a las hijas.
+
+      .. digraph:: g4_3
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         edge [ color = gray40 ];
+         node [
+            shape     = record,
+            width     = 0,
+            height    = 0,
+            style     = filled,
+            fillcolor = gray25,
+            fontcolor = white
+         ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+               fontcolor = black,
+            ];
+
+            subgraph marked {
+               node [ fillcolor = white, fontcolor = black ];
+               h1; h2;
+            };
+
+            h1 [ label = "h1\n0|<l> l|<r> r" ];
+            h2 [ label = "h2\n1|<l> l|<r> r" ];
+            h3 [ label = "h3\n1|<l> l\n*|<r> r" ];
+            h4 [ label = "h4\n1|<l> l|<r> r" ];
+            h5 [ label = "h5\n2|<l> l|<r> r" ];
+            h6 [ label = "h6\n1|<l> l|<r> r" ];
+
+            root:r0 -> h1 [ style = invis ];
+            h1:l -> h2 [ style = invis ];
+            h1:r -> h3 [ style = invis ];
+            root:r1 -> h3;
+            h2:l -> h4 [ style = bold, color = black ];
+            h2:r -> h5;
+            h3:l -> h2 [ style = invis ];
+            h3:l -> h5 [ style = dotted, color = black ];
+            h3:r -> h6;
+
+         }
+
+
+.. fig:: fig:gc-rc-up-2
+
+   Cambio en la referencia ``h2.l`` :math:`\to` ``h2`` a ``h2.l``
+   :math:`\to` ``h5`` (parte 2).
+
+   .. subfig::
+
+      Se decrementa el contador de ``h4`` quedando en 0, pasa a ser
+      *basura*. Se continúa con ``h5``.
+
+      .. digraph:: g4_4
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         edge [ color = gray40 ];
+         node [
+            shape     = record,
+            width     = 0,
+            height    = 0,
+            style     = filled,
+            fillcolor = gray25,
+            fontcolor = white
+         ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+               fontcolor = black,
+            ];
+
+            subgraph marked {
+               node [ fillcolor = white, fontcolor = black ];
+               h1; h2; h4;
+            };
+
+            h1 [ label = "h1\n0|<l> l|<r> r" ];
+            h2 [ label = "h2\n1|<l> l|<r> r" ];
+            h3 [ label = "h3\n1|<l> l\n*|<r> r" ];
+            h4 [ label = "h4\n0|<l> l|<r> r" ];
+            h5 [ label = "h5\n2|<l> l|<r> r" ];
+            h6 [ label = "h6\n1|<l> l|<r> r" ];
+
+            root:r0 -> h1 [ style = invis ];
+            h1:l -> h2 [ style = invis ];
+            h1:r -> h3 [ style = invis ];
+            root:r1 -> h3;
+            h2:l -> h4 [ style = invis ];
+            h2:r -> h5 [ style = bold, color = black ];
+            h3:l -> h2 [ style = invis ];
+            h3:l -> h5 [ style = dotted, color = black ];
+            h3:r -> h6;
+
+         }
+
+   .. subfig::
+
+      Se decrementa el contador de ``h5`` pero sigue siendo mayor que 0.
+
+      .. digraph:: g4_5
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         edge [ color = gray40 ];
+         node [
+            shape     = record,
+            width     = 0,
+            height    = 0,
+            style     = filled,
+            fillcolor = gray25,
+            fontcolor = white
+         ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+               fontcolor = black,
+            ];
+
+            subgraph marked {
+               node [ fillcolor = white, fontcolor = black ];
+               h1; h2; h4;
+            };
+
+            h1 [ label = "h1\n0|<l> l|<r> r" ];
+            h2 [ label = "h2\n1|<l> l|<r> r" ];
+            h3 [ label = "h3\n1|<l> l\n*|<r> r" ];
+            h4 [ label = "h4\n0|<l> l|<r> r" ];
+            h5 [ label = "h5\n1|<l> l|<r> r" ];
+            h6 [ label = "h6\n1|<l> l|<r> r" ];
+
+            root:r0 -> h1 [ style = invis ];
+            h1:l -> h2 [ style = invis ];
+            h1:r -> h3 [ style = invis ];
+            root:r1 -> h3;
+            h2:l -> h4 [ style = invis ];
+            h2:r -> h5 [ style = invis ];
+            h3:l -> h5 [ style = bold, color = black ];
+            h3:l -> h2 [ style = invis ];
+            h3:r -> h6;
+
+         }
+
+   .. subfig::
+
+      Se termina por actualizar la referencia de ``h3.l`` para que apunte
+      a ``h5``.
+
+      .. digraph:: g4_6
+
+         margin = 0;
+         ratio  = fill;
+         size   = "1.9,2.6";
+         edge [ color = gray40 ];
+         node [
+            shape     = record,
+            width     = 0,
+            height    = 0,
+            style     = filled,
+            fillcolor = gray25,
+            fontcolor = white
+         ];
+
+         subgraph cluster_all {
+
+            root [
+               label     = "root\nset|<r0> r0|<r1> r1",
+               style     = filled,
+               fillcolor = gray96,
+               fontcolor = black,
+            ];
+
+            subgraph marked {
+               node [ fillcolor = white, fontcolor = black ];
+               h1; h2; h4;
+            };
+
+            h1 [ label = "h1\n0|<l> l|<r> r" ];
+            h1 [ label = "h1\n0|<l> l|<r> r" ];
+            h2 [ label = "h2\n0|<l> l|<r> r" ];
+            h3 [ label = "h3\n1|<l> l|<r> r" ];
+            h4 [ label = "h4\n0|<l> l|<r> r" ];
+            h5 [ label = "h5\n1|<l> l|<r> r" ];
+            h6 [ label = "h6\n1|<l> l|<r> r" ];
+
+            root:r0 -> h1 [ style = invis ];
+            h1:l -> h2 [ style = invis ];
+            h1:r -> h3 [ style = invis ];
+            root:r1 -> h3;
+            h2:l -> h4 [ style = invis ];
+            h2:r -> h5 [ style = invis ];
+            h3:l -> h5;
+            h3:l -> h2 [ style = invis ];
+            h3:r -> h6;
+
+         }
+
+
+Marcado y barrido
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 TODO
 
 
 
-Compactado
+Copia/Semi-espacio
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 TODO
 
 
 
+.. _ref_gc_art:
+
 Estado del arte
 ----------------------------------------------------------------------------
 
@@ -142,7 +1451,152 @@ Estado del arte
 
 TODO
 
+Clasificación
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+La cantidad de clasificaciones que pueden hacerse respecto a algoritmos de
+recolección de basura son muy grandes. Muchas de estas clasificaciones se
+superponen entre sí, habiendo algoritmos que aplican muchas técnicas a la
+vez o recolectores híbridos que aplican una técnica para algún tipo de
+objeto y otra para otros.
+
+A continuación se enumeran las clasificaciones más importantes que se
+pudieron observar en la investigación sobre el `estado del arte`_.
+
+.. _ref_gc_direct:
+
+Directa / indirecta
+^^^^^^^^^^^^^^^^^^^
+
+Generalmente se llama recolección **directa** a aquella en la cual el
+compilador o lenguaje instrumenta al *mutator* de forma tal que la
+información de conectividad se mantenga activamente cada vez que hay un
+cambio en él. Normalmente se utiliza un contador de referencia en cada
+celda para este propósito, permitiendo almacenar en todo momento la
+cantidad de nodos que apuntan a ésta (dejando de lado el :ref:`problema de
+los ciclos <ref_gc_rc_cycles>`). Esto permite reclamar una celda
+instantáneamente cuando el *mutator* deja de hacer referencia a ella. Este
+tipo de recolectores son, inherentemente :ref:`incrementales <ref_gc_inc>`.
+
+Por el contrario, los recolectores **indirectos** normalmente no
+interfieren con el *mutator* en cada actualización del grafo de
+conectividad (exceptuando algunos :ref:`recolectores incrementales
+<ref_gc_inc>` que a veces necesitan instrumentar el *mutator* pero no para
+mantener el estado del grafo de conectividad completo). La recolección se
+dispara usualmente cuando el *mutator* requiere alocar memoria pero no hay
+más memoria libre conocida disponible y el recolector se encarga de generar
+la información de conectividad desde cero para determinar qué celdas son
+*basura*.
+
+Estas son las dos grandes familias de recolectores, también conocidos como
+`conteo de referencias`_ (directa) y *traicing garbage collection*
+(indirecta).
+
+
+.. _ref_gc_inc:
+
+Incremental
+^^^^^^^^^^^
+
+Recolección incremental es aquella que se realiza de forma intercalada con
+el *mutator*. En general el propósito es disminuir el tiempo de las pausas
+causadas por el recolector (aunque generalmente el resultado es un mayor
+costo en tiempo total de recolección).
+
+De los `algoritmos clásicos`_ el único que es incremental en su forma más
+básica es el `conteo de referencias`_. Otros recolectores pueden hacerse
+incrementales de diversas maneras, pero en general consta de hacer parte
+del trabajo de escanear el grafo de conectividad cada vez que el *mutator*
+aloca memoria. En general para hacer esto es también necesario instrumentar
+al *mutator* de forma tal que informe al recolector cada vez que cambia el
+grafo de conectividad, para que éste pueda marcar al sub-grafo afectado por
+el cambio como *desactualizado* y así re-escanearlo nuevamente en la
+próxima iteración.
+
+En general la eficiencia de los recolectores incrementales disminuye
+considerablemente cuando el *mutator* actualiza muy seguido el grafo de
+conectividad, porque debe re-escanear sub-grafos que ya había escaneado una
+y otra vez. A esto se debe también que en general el tiempo de
+procesamiento total de un recolector incremental sea mayor que uno no
+incremental, aunque el tiempo de pausa de una recolección sea menor.
+
+
+.. _ref_gc_concurrent:
+
+Concurrente / *stop-the-world*
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Los recolectores concurrentes son aquellos que pueden correr en paralelo
+con el *mutator*. Por el contrario, aquellos que pausan el *mutator* para
+realizar la recolección son usualmente denominados *stop-the-world* (*parar
+el mundo*), haciendo referencia a que pausan todos los hilos del *mutator*
+para poder escanear el grafo de conectividad de forma consistente.
+
+Para lograr que un recolector sea concurrente generalmente el mecanismo es
+similar al necesario para hacer un :ref:`recolector incremental
+<ref_gc_inc>`: hay que instrumentar al *mutator* para que informe al
+recolector cuando se realiza algún cambio en el grafo de conectividad, de
+forma tal que pueda volver a escanear el sub-grafo afectado por el cambio.
+
+Esto también trae como consecuencia el incremento en el tiempo total que
+consume el recolector, debido a la necesidad de re-escanear sub-grafos que
+han sido modificados.
+
+
+Cloning
+
+
+1. Instrumentar el *mutator* de forma tal de que informe al recolector cada
+   vez que cambia el grafo de conectividad, de manera que pueda re-escanear
+   el sub-grafo que afectado por el cambio.
+
+2. Tomar una foto de la memoria y escanear esa foto estática en vez de la
+   memoria original, de forma que cualquier cambio en del *mutator* no
+   afecte la vista de la memoria del recolector.
+
+Ambos métodos tienen ventajas y desventajas. El primero no necesita hacer
+una copia costosa de la memoria, pero existe la posibilidad de que el
+recolector nunca pueda escanear el grafo de conectividad completo, si el
+*mutator* modifica el grafo más rápido de lo que el recolector lo escanea.
+Si bien hay formas de evitar esto, es usual que el trabajo de recolección
+se vea considerablemente incrementado por la cantidad de veces que hay que
+re-escanear el grafo. El segundo método puede ser costoso por las copias
+necesarias (aunque esto puede ser mitigado empleando alguna forma de *COW*
+[#gccow]_) y existe la posibilidad de que no se recuperen celdas que el
+*mutator*
+
+
+.. [#gccow] *COW* (del inglés *Copy On Write* o *copiar al escribir*) es
+   una técnica muy utilizada para disminuir las copias innecesarias,
+   evitando hacer la copia hasta que haya una modificación. Mientras no
+   hayan modificaciones no es necesario realizar una copia porque todos los
+   interesados verán la misma información. Esta técnica es utilizada por el
+   *kernel* de Linux_ por ejemplo, para que varias instancias de un mismo
+   proceso puedan compartir la memoria.
+
+
+Boehm, H., and M. Weiser, "Garbage Collection in an Uncooperative
+Environment", Software Practice & Experience, September 1988, pp. 807-820.
+http://www.hpl.hp.com/personal/Hans_Boehm/spe_gc_paper/preprint.pdf
+(http://www.hpl.hp.com/personal/Hans_Boehm/spe_gc_paper/)
+
+Boehm, H., "Space Efficient Conservative Garbage Collection", Proceedings
+of the ACM SIGPLAN '93 Conference on Programming Language Design and
+Implementation, SIGPLAN Notices 28, 6 (June 1993), pp. 197-206.
+http://www.hpl.hp.com/personal/Hans_Boehm/gc/papers/pldi93.ps.Z
+
+Boehm, H., A. Demers, and S. Shenker, "Mostly Parallel Garbage Collection",
+Proceedings of the ACM SIGPLAN '91 Conference on Programming Language
+Design and Implementation, SIGPLAN Notices 26, 6 (June 1991), pp. 157-164.
+http://www.hpl.hp.com/personal/Hans_Boehm/gc/papers/pldi91.ps.Z
+
+Implementación de Bohem GC
+http://www.hpl.hp.com/personal/Hans_Boehm/gc/gcdescr.html
+
+Interfaz de Bohem GC
+http://www.hpl.hp.com/personal/Hans_Boehm/gc/gcinterface.html
+
 
 .. include:: links.rst
 
-.. vim: set ts=2 sts=2 sw=2 et tw=75 :
+.. vim: set ts=3 sts=3 sw=3 et tw=78 :