Articulo de referencia

Conteo de referencias

En informática , el conteo de referencias es una técnica de programación que consiste en almacenar el número de referencias , punteros o identificadores a un recurso, como un ob...

En informática , el conteo de referencias es una técnica de programación que consiste en almacenar el número de referencias , punteros o identificadores a un recurso, como un objeto, un bloque de memoria, espacio en disco, etc.

En los algoritmos de recolección de basura , se pueden utilizar contadores de referencias para liberar objetos que ya no son necesarios.

Ventajas y desventajas

La principal ventaja del conteo de referencias sobre la recolección de basura por rastreo es que los objetos se recuperan tan pronto como ya no se pueden referenciar, de forma incremental, sin largas pausas para los ciclos de recolección y con una vida útil claramente definida para cada objeto. En aplicaciones en tiempo real o sistemas con memoria limitada, esto es importante para mantener la capacidad de respuesta. El conteo de referencias también es una de las formas más sencillas de administrar la memoria. Además, permite una administración eficaz de recursos que no son memoria, como los objetos del sistema operativo, que suelen ser mucho más escasos que la memoria (los sistemas de recolección de basura por rastreo usan finalizadores para esto, pero la recuperación tardía puede causar problemas). Los conteos de referencias ponderados son una buena solución para la recolección de basura en un sistema distribuido.

Ejemplo de lista circular de una tesis de maestría de 1985. [ 1 ] Los rectángulos denotan pares cons , con contadores de referencias. Incluso si se elimina el puntero superior izquierdo entrante, todos los contadores permanecen >0.

Los ciclos de recolección de basura de rastreo se activan con demasiada frecuencia si el conjunto de objetos activos ocupa la mayor parte de la memoria disponible; se requiere espacio adicional para que sea eficiente. El rendimiento del conteo de referencias no se deteriora a medida que disminuye la cantidad total de espacio libre. [ 2 ]

Los recuentos de referencias también son información útil para optimizar el rendimiento en tiempo de ejecución. Por ejemplo, los sistemas que dependen en gran medida de objetos inmutables , como muchos lenguajes de programación funcional, pueden sufrir una penalización en eficiencia debido a las copias frecuentes. Sin embargo, si el compilador (o el sistema de ejecución ) sabe que un objeto en particular tiene solo una referencia (como suele ocurrir en muchos sistemas) y que esta se pierde al mismo tiempo que se crea un objeto nuevo similar (como en la instrucción `append` de una cadena ), puede reemplazar la operación con una modificación del objeto original.str ← str + "a"

El conteo de referencias en su forma ingenua tiene tres desventajas principales con respecto a la recolección de basura por rastreo, y ambas requieren mecanismos adicionales para su mitigación:

  • Las frecuentes actualizaciones que implica son una fuente de ineficiencia. Si bien los recolectores de basura de rastreo pueden afectar gravemente la eficiencia mediante el cambio de contexto y los fallos en la línea de caché, recolectan basura con relativa poca frecuencia, mientras que el acceso a los objetos se realiza continuamente. Además, aunque menos importante, el conteo de referencias requiere que cada objeto administrado en memoria reserve espacio para dicho conteo. En los recolectores de basura de rastreo, esta información se almacena implícitamente en las referencias que apuntan a ese objeto, lo que ahorra espacio, si bien los recolectores de basura de rastreo, en particular los incrementales, pueden requerir espacio adicional para otros fines.
  • El algoritmo ingenuo descrito anteriormente no puede manejarciclos de referencia ,un objeto que se refiere directa o indirectamente a sí mismo. Un mecanismo que dependa únicamente de los recuentos de referencias nunca considerará las cadenas cíclicas de objetos para su eliminación, ya que su recuento de referencias está garantizado para permanecer distinto de cero (véase la imagen). Existen métodos para abordar este problema, pero también pueden aumentar la sobrecarga y la complejidad del recuento de referencias ; por otro lado, estos métodos solo deben aplicarse a los datos que podrían formar ciclos, a menudo un pequeño subconjunto de todos los datos. Un método de este tipo es el uso dereferencias débiles, mientras que otro implica el uso de unde marcado y barridoque se llama con poca frecuencia para limpiar.
  • En un entorno concurrente, todas las actualizaciones de los contadores de referencias y todas las modificaciones de punteros deben ser operaciones atómicas , lo que conlleva un coste adicional. Existen tres razones para los requisitos de atomicidad. Primero, un campo de contador de referencias puede ser actualizado por múltiples hilos, por lo que debe utilizarse una instrucción atómica adecuada, como una operación de comparación e intercambio (que es costosa), para actualizar los contadores. Segundo, debe quedar claro qué objeto pierde una referencia para que su contador de referencias pueda decrementarse adecuadamente. Sin embargo, determinar este objeto no es trivial en un entorno donde múltiples hilos intentan modificar la misma referencia (es decir, cuando son posibles las condiciones de carrera). Finalmente, existe una sutil condición de carrera en la que un hilo obtiene un puntero a un objeto, pero antes de incrementar el contador de referencias del objeto, todas las demás referencias a este objeto son eliminadas concurrentemente por otros hilos y el objeto es recuperado, lo que provoca que dicho hilo incremente el contador de referencias de un objeto recuperado.

Además de esto, si la memoria se asigna desde una lista de memorias libres, el conteo de referencias sufre de una localidad deficiente. El conteo de referencias por sí solo no puede mover objetos para mejorar el rendimiento de la caché, por lo que los recolectores de alto rendimiento también implementan un recolector de basura de rastreo. La mayoría de las implementaciones (como las de PHP y Objective-C) sufren de un rendimiento deficiente de la caché ya que no implementan la copia de objetos. [ 3 ]

Interpretación de gráficos

Al trabajar con esquemas de recolección de basura, suele ser útil pensar en el grafo de referencia , que es un grafo dirigido donde los vértices son objetos y existe una arista desde un objeto  A a un objeto  B si A tiene una referencia a  B. También tenemos un vértice o vértices especiales que representan las variables locales y las referencias que mantiene el sistema en tiempo de ejecución, y ninguna arista apunta a estos nodos, aunque sí pueden apuntar desde ellos a otros nodos.

En este contexto, el conteo de referencias simple de un objeto es el grado de entrada de su vértice. Eliminar un vértice es como recoger un objeto. Solo se puede hacer cuando el vértice no tiene aristas entrantes, por lo que no afecta el grado de salida de ningún otro vértice, pero sí puede afectar el grado de entrada de otros vértices, lo que provocaría que sus objetos correspondientes también se recolecten si su grado de entrada también se convierte en 0 como resultado.

El componente conectado que contiene el vértice especial alberga los objetos que no se pueden recolectar, mientras que los demás componentes conectados del grafo solo contienen basura. Si se implementa un algoritmo de recolección de basura por conteo de referencias, cada uno de estos componentes de basura debe contener al menos un ciclo; de lo contrario, se recolectarían tan pronto como su contador de referencias (es decir, el número de aristas entrantes) llegara a cero.

Cómo solucionar la ineficiencia de las actualizaciones

Incrementar y decrementar los contadores de referencias cada vez que se crea o destruye una referencia puede afectar significativamente el rendimiento. Estas operaciones no solo consumen tiempo, sino que también perjudican el rendimiento de la caché y pueden provocar sobrecargas en la canalización . Incluso las operaciones de solo lectura, como calcular la longitud de una lista, requieren un gran número de lecturas y escrituras para actualizar las referencias con un conteo de referencias simple.

Una técnica sencilla consiste en que el compilador combine varias actualizaciones de referencias cercanas en una sola. Esto resulta especialmente eficaz para referencias que se crean y se destruyen rápidamente. Sin embargo, es importante colocar la actualización combinada en la posición correcta para evitar una liberación prematura de memoria.

El método de conteo de referencias de Deutsch-Bobrow aprovecha el hecho de que la mayoría de las actualizaciones del conteo de referencias se generan mediante referencias almacenadas en variables locales. Ignora estas referencias y solo cuenta las que se encuentran en estructuras de datos. Sin embargo, antes de eliminar un objeto con conteo de referencias cero, el sistema debe verificar, mediante un escaneo de la pila y los registros, que no exista ninguna otra referencia al mismo.

Otra técnica ideada por Henry Baker implica incrementos diferidos , [ 4 ] en los que las referencias almacenadas en variables locales no incrementan inmediatamente el contador de referencias correspondiente, sino que lo posponen hasta que sea necesario. Si dicha referencia se destruye rápidamente, no es necesario actualizar el contador. Esto elimina una gran cantidad de actualizaciones asociadas con referencias de corta duración (como el ejemplo anterior de conteo de longitud de lista). Sin embargo, si dicha referencia se copia en una estructura de datos, el incremento diferido debe realizarse en ese momento. También es fundamental realizar el incremento diferido antes de que el contador del objeto llegue a cero, para evitar una liberación prematura.

Levanoni y Petrank obtuvieron una drástica disminución en la sobrecarga de las actualizaciones del contador . [ 5 ] [ 6 ] Introdujeron el método de coalescencia de actualizaciones que coalesce muchas de las actualizaciones redundantes del contador de referencias. Consideremos un puntero que en un intervalo dado de la ejecución se actualiza varias veces. Primero apunta a un objeto O1, luego a un objeto O2, y así sucesivamente hasta que al final del intervalo apunta a algún objeto On. Un algoritmo de conteo de referencias normalmente ejecutaría rc(O1)--, rc(O2)++, rc(O2)--, rc(O3)++, rc(O3)--, ..., rc(On)++. Pero la mayoría de estas actualizaciones son redundantes. Para que el contador de referencias se evalúe correctamente al final del intervalo, es suficiente con realizar rc(O1)--y rc(On)++. El resto de las actualizaciones son redundantes.

En 2001, Levanoni y Petrank demostraron cómo utilizar la coalescencia de actualizaciones en un recolector de contadores de referencias. Al emplear la coalescencia de actualizaciones con un tratamiento adecuado de los nuevos objetos, se elimina más del 99 % de las actualizaciones del contador en las pruebas de rendimiento típicas de Java.

Curiosamente, la coalescencia de actualizaciones también elimina la necesidad de emplear operaciones atómicas durante las actualizaciones de punteros en un entorno concurrente, lo que resuelve los problemas de conteo de referencias en dicho entorno. Por lo tanto, la coalescencia de actualizaciones resuelve el tercer problema del conteo de referencias ingenuo (es decir, una sobrecarga costosa en un entorno concurrente). Levanoni y Petrank presentaron un algoritmo mejorado que puede ejecutarse concurrentemente con aplicaciones multihilo empleando únicamente sincronización fina. [ 7 ]

El método de conteo de referencias ulteriores de Blackburn y McKinley de 2003 [ 8 ] combina el conteo de referencias diferido con un grupo de copia, observando que la mayoría de las mutaciones de punteros ocurren en objetos jóvenes. Este algoritmo logra un rendimiento comparable al de los recolectores de copia generacionales más rápidos con los bajos tiempos de pausa acotados del conteo de referencias.

Cómo gestionar los ciclos de referencia

Quizás la forma más obvia de manejar los ciclos de referencia sea diseñar el sistema para evitar su creación. Un sistema puede prohibir explícitamente los ciclos de referencia; los sistemas de archivos con enlaces duros suelen hacerlo. El uso juicioso de referencias "débiles" (no contabilizadas) también puede ayudar a evitar los ciclos de retención; el marco Cocoa , por ejemplo, recomienda usar referencias "fuertes" para las relaciones padre-hijo y referencias "débiles" para las relaciones hijo-padre. [ 9 ]

Los sistemas también pueden diseñarse para tolerar o corregir los ciclos que generan. Los desarrolladores pueden diseñar código para eliminar explícitamente las referencias en una estructura de datos cuando ya no sea necesaria, aunque esto implica el seguimiento manual del ciclo de vida de dicha estructura. Esta técnica puede automatizarse creando un objeto "propietario" que se encargue de la eliminación al ser destruido; por ejemplo, el destructor de un objeto Graph podría borrar las aristas de sus GraphNodes, rompiendo así los ciclos de referencia en el grafo. Incluso se pueden ignorar los ciclos en sistemas con ciclos de vida cortos y una pequeña cantidad de datos cíclicos innecesarios, especialmente cuando el sistema se desarrolló utilizando una metodología que evita las estructuras de datos cíclicas siempre que sea posible, generalmente a costa de la eficiencia.

Los informáticos también han descubierto formas de detectar y recolectar ciclos de referencia automáticamente, sin necesidad de modificar el diseño de la estructura de datos. Una solución sencilla consiste en utilizar periódicamente un recolector de basura de rastreo para recuperar los ciclos; dado que los ciclos suelen ocupar una cantidad relativamente pequeña de espacio recuperado, el recolector puede ejecutarse con mucha menos frecuencia que un recolector de basura de rastreo convencional.

Bacon describe un algoritmo de recolección de ciclos para el conteo de referencias con similitudes a los recolectores de rastreo, incluyendo los mismos límites de tiempo teóricos. Se basa en la observación de que un ciclo solo puede aislarse cuando un contador de referencias se decrementa a un valor distinto de cero. Todos los objetos en los que esto ocurre se colocan en una lista de raíces , y luego el programa busca periódicamente ciclos entre los objetos accesibles desde las raíces. Sabe que ha encontrado un ciclo que puede recolectarse cuando al decrementar todos los contadores de referencias en un ciclo de referencias, todos se reducen a cero. [ 10 ] Una versión mejorada de este algoritmo por Paz et al. [ 11 ] puede ejecutarse concurrentemente con otras operaciones y mejora su eficiencia utilizando el método de coalescencia de actualización de Levanoni y Petrank. [ 5 ] [ 6 ]

Formas variantes

Si bien es posible ampliar los conteos de referencia simples de diversas maneras, a menudo se puede encontrar una mejor solución realizando el conteo de referencia de una forma fundamentalmente diferente. Aquí describimos algunas de las variantes del conteo de referencia, así como sus ventajas e inconvenientes.

Conteo de referencia ponderado

En el conteo de referencias ponderado, a cada referencia se le asigna un peso , y cada objeto registra no la cantidad de referencias que lo apuntan, sino el peso total de dichas referencias. La referencia inicial a un objeto recién creado tiene un peso elevado, como 2¹⁶ . Cada vez que se copia esta referencia, la mitad del peso se asigna a la nueva referencia y la otra mitad permanece en la antigua. Dado que el peso total no cambia, no es necesario actualizar el conteo de referencias del objeto.

Al destruir una referencia, el peso total disminuye en la cantidad de esa referencia. Cuando el peso total llega a cero, todas las referencias se han destruido. Si se intenta copiar una referencia con un peso de 1, la referencia debe aumentar su peso sumándole un valor, luego sumando este nuevo peso a la referencia y, finalmente, dividiéndola. Una alternativa en esta situación es crear un objeto de referencia de indirección , cuya referencia inicial se crea con un peso elevado que posteriormente se puede dividir.

La propiedad de no necesitar acceder al contador de referencias al copiar una referencia resulta especialmente útil cuando el acceso al contador de referencias del objeto es costoso, por ejemplo, porque se encuentra en otro proceso, en disco o incluso a través de una red. También puede ayudar a aumentar la concurrencia al evitar que muchos hilos bloqueen el contador de referencias para incrementarlo. Por lo tanto, el conteo de referencias ponderado es más útil en aplicaciones paralelas, multiproceso, de bases de datos o distribuidas.

El principal problema del conteo de referencias ponderado simple es que destruir una referencia aún requiere acceder al contador de referencias, y si se destruyen muchas referencias, esto puede causar los mismos cuellos de botella que buscamos evitar. Algunas adaptaciones del conteo de referencias ponderado buscan evitar esto transfiriendo peso de una referencia que está a punto de ser eliminada a una referencia activa.

El conteo de referencia ponderado fue ideado independientemente por Bevan [ 12 ] y Watson y Watson [ 13 ] en 1987.

Conteo de referencias indirecto

En el conteo indirecto de referencias, es necesario mantener un registro del origen de la referencia. Esto significa que se conservan dos referencias al objeto: una directa, que se utiliza para las invocaciones; y una indirecta, que forma parte de un árbol de difusión, como en el algoritmo de Dijkstra-Scholten , que permite al recolector de basura identificar objetos obsoletos. Este enfoque evita que un objeto se descarte prematuramente.

Ejemplos de uso

Recogida de basura

Como algoritmo de recolección de datos, el conteo de referencias registra, para cada objeto, la cantidad de referencias que otros objetos mantienen hacia él. Si el contador de referencias de un objeto llega a cero, el objeto se vuelve inaccesible y puede ser destruido.

Cuando se destruye un objeto, el contador de referencias de todos los objetos a los que hace referencia también disminuye. Por ello, eliminar una sola referencia puede liberar un gran número de objetos. Una modificación común permite que el conteo de referencias sea incremental: en lugar de destruir un objeto en cuanto su contador de referencias llega a cero, se añade a una lista de objetos sin referencias y, periódicamente (o según sea necesario), se destruyen uno o más elementos de esta lista.

Los contadores de referencias simples requieren actualizaciones frecuentes. Cada vez que se destruye o se sobrescribe una referencia, el contador de referencias del objeto al que hace referencia disminuye, y cada vez que se crea o se copia una, el contador de referencias del objeto al que hace referencia aumenta.

El conteo de referencias también se utiliza en sistemas de archivos y sistemas distribuidos, donde la recolección de basura de rastreo completo no incremental es demasiado lenta debido al tamaño del grafo de objetos y la baja velocidad de acceso. [ 14 ]

Modelo de objetos de componentes

El modelo de objetos componentes (COM) y WinRT de Microsoft hacen un uso generalizado del conteo de referencias. De hecho, dos de los tres métodos que deben proporcionar todos los objetos COM (en la interfaz IUnknown ) incrementan o decrementan el contador de referencias. Gran parte del shell de Windows y muchas aplicaciones de Windows (incluidos MS Internet Explorer , MS Office e innumerables productos de terceros) están basados ​​en COM, lo que demuestra la viabilidad del conteo de referencias en sistemas a gran escala.

Una de las principales motivaciones para el conteo de referencias en COM es permitir la interoperabilidad entre diferentes lenguajes de programación y sistemas de ejecución. Un cliente solo necesita saber cómo invocar los métodos del objeto para gestionar su ciclo de vida; por lo tanto, el cliente queda completamente abstraído del asignador de memoria que utilice la implementación del objeto COM. Como ejemplo típico, un programa de Visual Basic que utiliza un objeto COM es independiente de si dicho objeto fue asignado (y posteriormente liberado) por un asignador de C++ o por otro componente de Visual Basic.

C++

C++ no realiza el conteo de referencias por defecto, cumpliendo así con su filosofía de no añadir funcionalidades que puedan generar sobrecargas cuando el usuario no las haya solicitado explícitamente. Se puede acceder a los objetos compartidos pero no propiedad de nadie mediante una referencia, un puntero sin formato o un iterador (una generalización conceptual de los punteros).

Sin embargo, del mismo modo, C++ proporciona formas nativas para que los usuarios opten por dicha funcionalidad: C++11 proporciona punteros inteligentes con conteo de referencias , a través de la std::shared_ptrclase, lo que permite la administración automática de memoria compartida de objetos asignados dinámicamente. Los programadores pueden usar esto junto con punteros débiles (a través de std::weak_ptr) para romper dependencias cíclicas. Los objetos que se asignan dinámicamente pero que no están destinados a ser compartidos pueden tener su ciclo de vida administrado automáticamente usando un std::unique_ptr.

Además, la semántica de movimiento de C++11 reduce aún más la necesidad de modificar los contadores de referencias al eliminar la copia profunda que se usa normalmente cuando una función devuelve un objeto, ya que permite una copia simple del puntero de dicho objeto.

Cacao (Objective-C)

Los frameworks Cocoa y Cocoa Touch de Apple (y frameworks relacionados, como Core Foundation ) utilizan el conteo manual de referencias, muy parecido a COM . Tradicionalmente, esto se lograba mediante el envío manual retainde releasemensajes a los objetos por parte del programador, pero el Conteo Automático de Referencias , una característica del compilador Clang que inserta automáticamente estos mensajes según sea necesario, se agregó en iOS 5 [ 15 ] y Mac OS X 10.7 . [ 16 ] Mac OS X 10.5 introdujo un recolector de basura de rastreo como alternativa al conteo de referencias, pero se dejó de usar en OS X 10.8 y se eliminó de la biblioteca de tiempo de ejecución de Objective-C en macOS Sierra . [ 17 ] [ 18 ] iOS nunca ha admitido un recolector de basura de rastreo.

Delfos

Delphi no es un lenguaje con recolección de basura automática, ya que los tipos definidos por el usuario aún deben asignarse y liberarse manualmente; sin embargo, ofrece recolección automática mediante conteo de referencias para algunos tipos integrados, como cadenas, arreglos dinámicos e interfaces , para facilitar su uso y simplificar la funcionalidad genérica de la base de datos. El programador decide si utiliza los tipos integrados; los programadores de Delphi tienen acceso completo a la administración de memoria de bajo nivel, como en C/C++. Por lo tanto, cualquier costo potencial del conteo de referencias de Delphi puede, si se desea, evitarse fácilmente.

Algunas de las razones por las que se pudo haber preferido el conteo de referencias a otras formas de recolección de basura en Delphi incluyen:

  • Entre los beneficios generales del recuento de referencias se incluye la recopilación rápida de datos.
  • Los ciclos no pueden ocurrir o no ocurren en la práctica porque ninguno de los tipos integrados con recolección de basura es recursivo. (Se podría crear un escenario así usando interfaces, pero no es un uso común).
  • El tamaño del código adicional necesario para el conteo de referencias es muy pequeño (en x86 nativo, normalmente una sola instrucción LOCK INC, LOCK DEC o LOCK XADD, lo que garantiza la atomicidad en cualquier entorno), y no se necesita ningún hilo de control separado para la recolección, como se necesitaría para un recolector de basura de rastreo.
  • Muchas instancias del tipo de dato más utilizado y gestionado por el recolector de basura, la cadena de caracteres, tienen una vida útil corta, ya que suelen ser valores intermedios en la manipulación de cadenas. Gran parte del uso local de cadenas podría optimizarse, pero el compilador actualmente no lo hace.
  • Se comprueba el contador de referencias de una cadena antes de modificarla. Esto permite modificar directamente las cadenas con un contador de referencias de 1, mientras que las cadenas con un contador de referencias mayor se copian antes de la modificación. De esta forma, se conserva el comportamiento habitual de las cadenas de Pascal antiguas , eliminando el coste de copiar la cadena en cada asignación.
  • Dado que la recolección de basura solo se realiza en tipos integrados, el conteo de referencias se puede integrar de manera eficiente en las rutinas de la biblioteca que se utilizan para manipular cada tipo de dato, lo que reduce la sobrecarga necesaria para actualizar los conteos de referencias. Además, gran parte de la biblioteca de tiempo de ejecución está escrita en lenguaje ensamblador optimizado manualmente.
  • El tipo de cadena se puede convertir a un puntero a carácter, lo que permite realizar operaciones de alto rendimiento. Esto es importante, ya que tanto Delphi como FPC implementan su RTL en Pascal. Otros tipos automatizados también ofrecen opciones de conversión similares.

GObject

El framework de programación orientada a objetos GObject implementa el conteo de referencias en sus tipos base, incluidas las referencias débiles . El incremento y decremento de referencias utiliza operaciones atómicas para garantizar la seguridad de los hilos. Gran parte del trabajo al escribir enlaces a GObject desde lenguajes de alto nivel consiste en adaptar el conteo de referencias de GObject para que funcione con el sistema de gestión de memoria propio del lenguaje.

El lenguaje de programación Vala utiliza el conteo de referencias GObject como su sistema principal de recolección de basura, junto con un manejo intensivo de cadenas mediante copias. [ 19 ]

Perl

Perl también utiliza el conteo de referencias, sin ningún manejo especial de las referencias circulares, aunque (al igual que en Cocoa y C++ mencionados anteriormente), Perl sí admite referencias débiles, lo que permite a los programadores evitar la creación de un ciclo.

PHP

PHP utiliza un mecanismo de conteo de referencias para la gestión interna de sus variables. [ 20 ] Desde PHP 5.3, implementa el algoritmo del artículo de Bacon mencionado anteriormente. PHP permite activar y desactivar la recolección de ciclos mediante funciones de usuario. También permite forzar manualmente la ejecución del mecanismo de purga.

$a = "nueva cadena" ; $b = $a ; xdebug_debug_zval ( "a" );

El ejemplo anterior dará como resultado:

a: (refcount=2, is_ref=0)='nueva cadena'

Pitón

Python también utiliza el conteo de referencias y ofrece detección de ciclos (y puede recuperar ciclos de referencia). [ 21 ]

Óxido

Al igual que otros lenguajes de bajo nivel, Rust no proporciona conteo de referencias por defecto. En cambio, cualquier tipo construido se descarta cuando sale del ámbito. Cuando un programador necesita definir el ámbito de un tipo construido, suele utilizar tiempos de vida.

Sin embargo, el lenguaje también ofrece varias alternativas a las formas complejas de gestión de memoria. La funcionalidad de conteo de referencias la proporcionan los tipos std::rc::Rcy std::sync::Arc, que son no atómicos y atómicos respectivamente.

Por ejemplo, el tipo std::rc::Rc<T>proporciona propiedad compartida de un valor de tipo T, asignado en el montón para múltiples referencias a sus datos. [ 22 ]

usar std :: rc :: Rc ;struct Cat { color : String , }fn main () { let cat = Cat { color : "black" . to_string () }; let cat = Rc :: new ( cat ); }

El uso de estas estructuras permite a los programadores evitar el cálculo del tiempo de vida con un coste mínimo en tiempo de ejecución. Ambos contadores de referencia llevan un registro del número de propietarios, ya que deben eliminarse cuando no quedan propietarios.

Una faceta destacable de estos tipos está relacionada con su uso como referencia compartida. En Rust, las referencias compartidas no pueden modificar los datos que contienen, por lo que std::rc::Rca menudo vienen agrupadas con std::cell::Celly std::sync::Arccon std::sync::Mutex, en contextos donde la mutabilidad interna es necesaria.

La mutabilidad interna sin std::cell::UnsafeCelltambién tiene costos de rendimiento, por lo que, para un rendimiento máximo, algunas aplicaciones pueden requerir una complejidad adicional. [ 23 ]

Ardilla

Squirrel utiliza el conteo de referencias con detección de ciclos. Este pequeño lenguaje es relativamente desconocido fuera de la industria de los videojuegos; sin embargo, es un ejemplo concreto de cómo el conteo de referencias puede ser práctico y eficiente (especialmente en entornos en tiempo real).

Rápido

Swift utiliza el conteo de referencias para rastrear y administrar la memoria de las instancias de clase, y proporciona la weakpalabra clave para crear referencias débiles. Las instancias de tipos de valor no utilizan el conteo de referencias. [ 24 ]

Tcl

Tcl 8 utiliza el conteo de referencias para la gestión de memoria de valores ( estructuras Tcl Obj ). Dado que los valores de Tcl son inmutables, es imposible que se formen ciclos de referencia y no se necesita ningún esquema de detección de ciclos. Las operaciones que reemplazarían un valor con una copia modificada generalmente se optimizan para modificar el original cuando su conteo de referencias indica que no se comparte. Las referencias se contabilizan a nivel de estructura de datos, por lo que no surgen los problemas con las actualizaciones muy frecuentes mencionados anteriormente.

Xojo

Xojo también utiliza el conteo de referencias, sin ningún manejo especial de las referencias circulares, aunque (al igual que en Cocoa y C++ mencionados anteriormente), Xojo sí admite referencias débiles, lo que permite a los programadores evitar la creación de un ciclo.

Sistemas de archivos

Muchos sistemas de archivos mantienen contadores de referencias a cualquier bloque o archivo en particular; por ejemplo, el contador de enlaces de inodo en los sistemas de archivos de estilo Unix , que generalmente se conocen como enlaces duros . Cuando el contador llega a cero, el archivo se puede liberar de forma segura. Si bien aún se pueden hacer referencias desde directorios , algunos sistemas Unix solo permiten referencias desde procesos activos, y puede haber archivos que existan fuera de la jerarquía del sistema de archivos.

Referencias

  1. Kevin G. Cassidy (diciembre de 1985). La viabilidad de la recuperación automática de almacenamiento con ejecución concurrente de programas en un entorno LISP (PDF) (tesis de maestría). Escuela Naval de Posgrado, Monterey/CA.Aquí: pág. 25
  2. Wilson, Paul R. (1992). "Técnicas de recolección de basura en uniprocesadores" . Actas del Taller Internacional sobre Gestión de Memoria . Londres, Reino Unido: Springer-Verlag. págs. 1–42 . ISBN  3-540-55940-X.Sección 2.1.
  3. Rifat Shahriyar, Stephen M. Blackburn, Xi Yang y Kathryn S. McKinley (2013). "Quitándose los guantes con el conteo de referencias Immix" (PDF) . 24.ª conferencia ACM SIGPLAN sobre sistemas, lenguajes y aplicaciones de programación orientada a objetos . OOPSLA 2013. doi : 10.1145/2509136.2509527 .{{cite conference}}: CS1 maint: varios nombres: lista de autores ( enlace )
  4. Henry Baker (septiembre de 1994). "Minimizing Reference Count Updating with Deferred and Anchored Pointers for Functional Data Structures". ACM SIGPLAN Notices . 29 (9): 38– 43. CiteSeerX 10.1.1.25.955 . doi : 10.1145/185009.185016 . S2CID 14448488 .  
  5. 1 2 Yossi Levanoni, Erez Petrank (2001). "Un recolector de basura de conteo de referencias sobre la marcha para Java" . Actas de la 16.ª conferencia ACM SIGPLAN sobre programación orientada a objetos, sistemas, lenguajes y aplicaciones . OOPSLA 2001. págs. 367–380 . doi : 10.1145/504282.504309 . 
  6. 1 2 Yossi Levanoni, Erez Petrank (2006). "Un recolector de basura de conteo de referencias sobre la marcha para Java" . ACM Trans. Program. Lang. Syst . 28 : 31–69 . CiteSeerX 10.1.1.15.9106 . doi : 10.1145/1111596.1111597 . S2CID 14777709 .  
  7. "Un recolector de basura con conteo de referencias en tiempo real para Java" (PDF) . Cs.technion.ac.il . Consultado el 24 de junio de 2017 .
  8. Stephen Blackburn; Kathryn McKinley (2003). "Conteo de referencias ulteriores: recolección de basura rápida sin largas esperas" (PDF) . Actas de la 18.ª conferencia anual ACM SIGPLAN sobre programación orientada a objetos, sistemas, lenguajes y aplicaciones . OOPSLA 2003. págs. 344–358 . doi : 10.1145/949305.949336 . ISBN  1-58113-712-5.
  9. "Biblioteca para desarrolladores de Mac" . Developer.apple.com . Consultado el 17 de diciembre de 2015 .
  10. Bacon, David F.; Rajan, VT (2001). "Recopilación de ciclos concurrentes en sistemas de conteo de referencias" (PDF) . ECOOP 2001 — Programación orientada a objetos . Lecture Notes in Computer Science. Vol. 2072. pp. 207–235 . doi : 10.1007/3-540-45337-7_12 . ISBN   978-3-540-42206-8Archivado del original (PDF) el 23 de julio de 2004.
  11. Harel Paz, David F. Bacon, Elliot K. Kolodner, Erez Petrank , VT Rajan (2007). "Una eficiente colección de ciclos sobre la marcha". ACM Transactions on Programming Languages ​​and Systems . 29 (4): 20–es. CiteSeerX 10.1.1.10.2777 . doi : 10.1145/1255450.1255453 . S2CID 4550008 .  {{cite journal}}: CS1 maint: varios nombres: lista de autores ( enlace )
  12. Bevan, DI (1987). «Recolección de basura distribuida mediante conteo de referencias». Volumen II: Lenguajes paralelos en PARLE: Arquitecturas y lenguajes paralelos en Europa . Eindhoven, Países Bajos: Springer-Verlag. pp. 176–187 . ISBN  0-387-17945-3.
  13. Watson, Paul; Watson, Ian (1987). «Un esquema eficiente de recolección de basura para arquitecturas informáticas paralelas». Volumen II: Lenguajes paralelos en PARLE: Arquitecturas y lenguajes paralelos Europa . Eindhoven, Países Bajos: Springer-Verlag. págs. 432–443 . ISBN  0-387-17945-3.
  14. Bruno, Rodrigo; Ferreira, Paulo (2018). "Un estudio sobre algoritmos de recolección de basura para entornos de big data". ACM Computing Surveys . 51 : 1–35 . doi : 10.1145/3156818 . S2CID 21388487 . 
  15. Archivado el 9 de junio de 2011 en Wayback Machine.
  16. "Biblioteca para desarrolladores de Mac" . Developer.apple.com . Consultado el 17 de diciembre de 2015 .
  17. Siracusa, John (25 de julio de 2012). "OS X 10.8 Mountain Lion: la reseña de Ars Technica" . Ars Technica . En la sección "Mejoras de Objective-C" . Consultado el 17 de noviembre de 2016 .
  18. "Notas de la versión de Xcode 8" . Apple Developer . 27 de octubre de 2016. Archivado del original el 19 de marzo de 2017. Consultado el 19 de marzo de 2017 .
  19. "Proyectos/Vala/Manejo de referencias - ¡GNOME Wiki!" . GNOME. 25 de mayo de 2015 . Consultado el 17 de diciembre de 2015 .
  20. "PHP: Conceptos básicos de conteo de referencias - Manual" . www.php.net . Consultado el 1 de octubre de 2020 .
  21. "1. Ampliando Python con C o C++ — Documentación de Python 2.7.11" . Docs.python.org. 5 de diciembre de 2015. Consultado el 17 de diciembre de 2015 .
  22. "std::rc - Rust" . doc.rust-lang.org . Consultado el 2 de noviembre de 2020 .
  23. "The Rust Reference" . 21 de julio de 2022. Mutabilidad interna. Archivado del original el 24 de marzo de 2024. Consultado el 22 de abril de 2024 .
  24. "Documentación" . docs.swift.org . Consultado el 6 de diciembre de 2023 .
  • Referencia del administrador de memoria: Guía para principiantes: Reciclaje: Recuento de referencias
  • Un recolector de basura con conteo de referencias en tiempo real para Java , Yossi Levanoni y Erez Petrank
  • Punteros de conteo de referencias atómicos: Un puntero de conteo de referencias sin bloqueo, sin asincronía, seguro para subprocesos y seguro para multiprocesadores , Kirk Reinholtz
  • Ampliación e integración del intérprete de Python: Ampliación de Python con C o C++: Conteo de referencias , Guido van Rossum
  • ¿Derribados? Recuperando el conteo de referencias en el ring , Rifat Shahriyar, Stephen M. Blackburn y Daniel Frampton.