¿Por qué recolección de basura si hay punteros inteligentes?

67

En estos días, se recolectan muchos idiomas. Incluso está disponible para C ++ por terceros. Pero C ++ tiene RAII y punteros inteligentes. Entonces, ¿qué sentido tiene usar la recolección de basura? ¿Está haciendo algo extra?

Y en otros lenguajes como C #, si todas las referencias se tratan como punteros inteligentes (manteniendo a un lado el RAII), por especificación y por implementación, ¿habrá alguna necesidad de recolectores de basura? Si no, ¿por qué no es así?

Gulshan
fuente
1
Una cosa que entendí después de hacer esta pregunta: los punteros inteligentes necesitan RAII para administrar la desasignación automática .
Gulshan
8
Los punteros inteligentes significan usar RAII para GC;)
Dario
Je, c # debería tener una opción para manejar toda la "recolección de basura" con RAII. Las referencias circulares se pueden detectar en el cierre de la aplicación, todo lo que necesitamos es ver qué asignaciones todavía están en la memoria después de que la clase Program.cs se haya desasignado. Luego, las referencias circulares se pueden reemplazar con algún tipo de referencias semanales.
AareP

Respuestas:

67

Entonces, ¿cuál es el punto de usar la recolección de basura?

Supongo que te refieres a los punteros inteligentes contados de referencia y notaré que son una forma (rudimentaria) de recolección de basura, así que responderé a la pregunta "¿cuáles son las ventajas de otras formas de recolección de basura sobre los punteros inteligentes contados de referencia"? en lugar.

  • Precisión . El recuento de referencias solo pierde ciclos, por lo que los punteros inteligentes contados con referencias perderán memoria en general a menos que se agreguen otras técnicas para capturar los ciclos. Una vez que se agregan esas técnicas, el beneficio de simplicidad del conteo de referencias ha desaparecido. Además, tenga en cuenta que los GC de conteo y rastreo de referencia basados ​​en el alcance recopilan valores en diferentes momentos, a veces el conteo de referencias se recolecta antes y a veces los GC de rastreo se recopilan antes.

  • Rendimiento . Los punteros inteligentes son una de las formas menos eficientes de recolección de basura, particularmente en el contexto de aplicaciones de subprocesos múltiples cuando los recuentos de referencias se generan atómicamente. Existen técnicas avanzadas de conteo de referencias diseñadas para aliviar esto, pero los GC de rastreo siguen siendo el algoritmo de elección en entornos de producción.

  • Latencia . Las implementaciones típicas de puntero inteligente permiten que los destructores se avalanchen, lo que resulta en tiempos de pausa ilimitados. Otras formas de recolección de basura son mucho más incrementales e incluso pueden ser en tiempo real, por ejemplo, la cinta de correr de Baker.

Jon Harrop
fuente
23
No puedo creer que esta respuesta haya sido la mejor respuesta. Muestra una total falta de comprensión de los punteros inteligentes de C ++ y hace afirmaciones que están tan fuera de sincronía con la realidad que es simplemente ridículo. Primero, el puntero inteligente que en una pieza bien diseñada de código C ++ será más dominante es el puntero único, no el puntero compartido. es.cppreference.com/w/cpp/memory/unique_ptr Y, en segundo lugar, no puedo creer que realmente reclame ventajas de 'rendimiento' y ventajas en tiempo real de la recolección de basura no determinista sobre punteros inteligentes.
user1703394
44
@ user1703394 Parece que el respondedor tenía en cuenta los punteros compartidos (correcta o incorrectamente, no estoy muy seguro de lo que sugieren los OP), que son menos eficaces que la recolección de basura no determinista.
Nathan Cooper
8
Estos son todos argumentos de hombre de paja y solo son válidos si ignora por completo la pregunta real o ignora los patrones de uso reales de los diferentes tipos de punteros inteligentes. La pregunta era sobre punteros inteligentes. Sí shared_ptr es un puntero inteligente y yes shared_ptr es el más costoso de los punteros inteligentes, pero no, no existe un argumento real para su uso generalizado cerca de hacer que cualquier argumento de rendimiento sea relevante. En serio, esta respuesta debe trasladarse a una pregunta sobre recuento de referencias. Es una pobre respuesta de conteo de referencias a una buena pregunta de puntero inteligente.
user1703394
44
"Los punteros inteligentes no son un concepto más amplio", ¿en serio? No tienes idea de cuánto socava esa declaración todos los argumentos posiblemente válidos que has hecho hasta ahora. Tal vez eche un vistazo a la propiedad de Rust y mueva la semántica: koerbitz.me/posts/... Es fácil para las personas con experiencia histórica con C ++ perderse el hecho de que C ++ 11 / C ++ 14 con su modelo de memoria, mejorado inteligente Los punteros y la semántica de movimiento es una bestia completamente diferente a sus predecesores. Eche un vistazo a cómo lo hace Rust, es más limpio que C ++ y proporciona una nueva perspectiva.
user1703394
66
@ user1703394: "Las pausas ilimitadas debido a destructores son una propiedad desafortunada de que RAII se utiliza para recursos que no son de memoria". No, esto no tiene nada que ver con recursos que no sean de memoria.
Jon Harrop
63

Como nadie lo ha mirado desde este ángulo, reformularé su pregunta: ¿por qué poner algo en el idioma si puede hacerlo en una biblioteca? Ignorando la implementación específica y los detalles sintácticos, GC / punteros inteligentes es básicamente un caso especial de esa pregunta. ¿Por qué definir un recolector de basura en el propio lenguaje si puede implementarlo en una biblioteca?

Hay un par de respuestas a esa pregunta. Lo más importante primero:

  1. Se asegura de que todo el código pueda usarlo para interoperar. Esta es, creo, la gran razón por la cual la reutilización de código y el intercambio de código realmente no despegaron hasta Java / C # / Python / Ruby. Las bibliotecas necesitan comunicarse, y el único idioma compartido confiable que tienen es el contenido de la especificación del idioma en sí (y, hasta cierto punto, su biblioteca estándar). Si alguna vez ha intentado reutilizar bibliotecas en C ++, es probable que haya experimentado el horrible dolor que no causa la semántica de memoria estándar. Quiero pasar una estructura a alguna lib. ¿Paso una referencia? ¿Puntero? scoped_ptr?smart_ptr? ¿Estoy pasando la propiedad, o no? ¿Hay alguna manera de indicar eso? ¿Qué pasa si la biblioteca necesita asignar? ¿Tengo que darle un asignador? Al no hacer que la administración de memoria forme parte del lenguaje, C ++ obliga a cada par de bibliotecas a negociar aquí su propia estrategia específica, y es realmente difícil lograr que todos estén de acuerdo. GC lo convierte en un completo problema.

  2. Puede diseñar la sintaxis a su alrededor. Debido a que C ++ no encapsula la administración de memoria en sí misma, tiene que proporcionar una gama de enlaces sintácticos para permitir que el código a nivel de usuario exprese todos los detalles. Tiene punteros, referencias, constoperadores de desreferenciación, operadores de indirección, dirección de dirección, etc. Si aplica la administración de memoria al lenguaje en sí, la sintaxis puede diseñarse en torno a eso. Todos esos operadores desaparecen y el lenguaje se vuelve más limpio y simple.

  3. Obtiene un alto retorno de la inversión. El valor que genera cualquier fragmento de código se multiplica por el número de personas que lo utilizan. Esto significa que cuantos más usuarios tenga, más puede permitirse gastar en una pieza de software. Cuando mueva una función al idioma, todos los usuarios del idioma la usarán. Esto significa que puede asignarle más esfuerzo del que podría a una biblioteca solo utilizada por un subconjunto de esos usuarios. Es por eso que lenguajes como Java y C # tienen máquinas virtuales absolutamente de primer nivel y recolectores de basura fantásticamente de alta calidad: el costo de desarrollarlos se amortiza en millones de usuarios.

munificente
fuente
Fantástica respuesta! Si tan solo pudiera votar más de una vez ...
Dean Harding
10
Vale la pena señalar que la recolección de basura en realidad no se implementa en el lenguaje C # en sí, sino en .NET Framework , específicamente Common Language Runtime (CLR).
Robert Harvey
66
@RobertHarvey: No está implementado por el lenguaje, pero no funcionaría sin la cooperación del lenguaje. Por ejemplo, el compilador debe incluir información que especifique, en cada punto del código, la ubicación de cada registro o desplazamiento de marco de pila que contenga una referencia a un objeto no fijado. Esa es una invariante absoluta, sin excepciones , que no podría sostenerse sin el soporte del idioma.
supercat
Una ventaja importante de que GC admite el lenguaje y el marco requerido es que garantiza que nunca existirán referencias a la memoria que pueda asignarse para algún otro propósito. Si se invoca Disposeun objeto que encapsula un mapa de bits, cualquier referencia a ese objeto será una referencia a un objeto de mapa de bits dispuesto. Si el objeto se eliminó prematuramente mientras otro código todavía espera usarlo, la clase de mapa de bits puede garantizar que el otro código falle de manera predecible . Por el contrario, usar una referencia a la memoria liberada es Comportamiento indefinido.
supercat
34

La recolección de basura básicamente significa que los objetos asignados se liberan automáticamente en algún momento después de que ya no se puede acceder a ellos.

Más exactamente, se lanzan cuando se vuelven inalcanzables para el programa, ya que los objetos referenciados circularmente nunca se liberarían de otra manera.

Los punteros inteligentes solo se refieren a cualquier estructura que se comporta como un puntero ordinario pero tiene alguna funcionalidad adicional adjunta. Estos incluyen , entre otros, desasignación, sino también copia en escritura, cheques encuadernados, ...

Ahora, como ha dicho, los punteros inteligentes se pueden usar para implementar una forma de recolección de basura.

Pero el tren del pensamiento va de la siguiente manera:

  1. La recolección de basura es algo genial, ya que es conveniente y tengo que ocuparme de menos cosas
  2. Por lo tanto: quiero la recolección de basura en mi idioma
  3. Ahora, ¿cómo puedo llevar GC a mi idioma?

Por supuesto, puedes diseñarlo así desde el principio. C # fue diseñado para ser recolectado como basura, por lo que solo newsu objeto y se lanzará cuando las referencias caigan fuera del alcance. Cómo se hace esto depende del compilador.

Pero en C ++, no se pretendía la recolección de basura. Si asignamos algún puntero int* p = new int;y queda fuera de alcance, pse elimina de la pila, pero nadie se ocupa de la memoria asignada.

Ahora lo único que tienes desde el principio son destructores deterministas . Cuando un objeto abandona el ámbito en el que se ha creado, se llama a su destructor. En combinación con las plantillas y la sobrecarga del operador, puede diseñar un objeto contenedor que se comporte como un puntero, pero utilice la funcionalidad de destructor para limpiar los recursos adjuntos (RAII). Llamas a este un puntero inteligente .

Todo esto es altamente específico de C ++: sobrecarga del operador, plantillas, destructores, ... En esta situación de lenguaje en particular, ha desarrollado punteros inteligentes para proporcionarle el GC que desea.

Pero si diseña un lenguaje con GC desde el principio, esto es simplemente un detalle de implementación. Simplemente dice que el objeto se limpiará y el compilador lo hará por usted.

Los punteros inteligentes como en C ++ probablemente ni siquiera serían posibles en lenguajes como C #, que no tienen ninguna destrucción determinista en absoluto (C # lo soluciona al proporcionar azúcar sintáctico para llamar .Dispose()a ciertos objetos). Los recursos no referenciados finalmente serán reclamados por el GC, pero no se definió cuándo ocurrirá exactamente esto.

Y esto, a su vez, puede permitir que el GC haga su trabajo de manera más eficiente. Al estar integrado más profundamente en el lenguaje que los punteros inteligentes, que están configurados encima, el GC .NET puede, por ejemplo, retrasar las operaciones de memoria y realizarlas en bloques para que sean más baratas o incluso mover la memoria para aumentar la eficiencia en función de la frecuencia con la que los objetos Se accede.

Darío
fuente
C # tiene una forma de destrucción determinista a través de IDisposabley using. Pero requiere un poco de esfuerzo del programador, por lo que generalmente solo se usa para recursos muy escasos, como los identificadores de conexión de la base de datos.
JSB ձոգչ
77
@JSBangs: Exactamente. Del mismo modo que C ++ crea punteros inteligentes alrededor de RAII para obtener GC, C # va por el otro lado y construye "eliminadores inteligentes" alrededor del GC para obtener RAII;) De hecho, es una pena que RAII sea tan difícil en C # ya que es excelente para la excepción. manejo seguro de recursos. F #, por ejemplo, intenta una IDisposablesintaxis más simple simplemente reemplazando la convencional let ident = valuepor use ident = value...
Dario
@Dario: "C # va para otro lado y construye 'dispositivos inteligentes' alrededor del GC para obtener RAII". RAII en C # usingno tiene nada que ver con la recolección de basura, simplemente llama a una función cuando una variable cae fuera del alcance al igual que los destructores en C ++.
Jon Harrop
1
@ Jon Harrop: ¿Por favor qué? La declaración que usted cita es sobre destructores simples de C ++, sin ningún conteo de referencias / punteros inteligentes / recolección de basura involucrada.
Dario
1
"La recolección de basura básicamente significa que los objetos asignados se liberan automáticamente cuando ya no se hace referencia a ellos. Más exactamente, se liberan cuando se vuelven inalcanzables para el programa, ya que los objetos referenciados circularmente nunca se liberarían". ... Más exacto sería decir que se lanzan automáticamente en algún momento después , no cuando . Tenga en cuenta que cuando implica que el reclamo ocurre de inmediato, cuando en realidad el reclamo ocurre mucho más tarde.
Todd Lehman
4

En mi opinión, hay dos grandes diferencias entre la recolección de basura y los punteros inteligentes que se usan para la administración de memoria:

  1. Los punteros inteligentes no pueden recolectar basura cíclica; lata de recolección de basura
  2. Los punteros inteligentes hacen todo el trabajo en los momentos de referencia, desreferenciación y desasignación, en el hilo de la aplicación; la recolección de basura no necesita

Lo primero significa que GC recolectará basura que los punteros inteligentes no lo harán; Si está utilizando punteros inteligentes, debe evitar crear este tipo de basura o estar preparado para manejarlo manualmente.

Esto último significa que no importa cuán inteligentes sean los punteros inteligentes, su funcionamiento ralentizará los hilos de trabajo en su programa. La recolección de basura puede diferir el trabajo y moverlo a otros hilos; eso le permite ser más eficiente en general (de hecho, el costo de tiempo de ejecución de un GC moderno es menor que un sistema libre / malloc normal, incluso sin la sobrecarga adicional de los punteros inteligentes), y hacer el trabajo que aún necesita hacer sin entrar en el forma de los hilos de la aplicación.

Ahora, tenga en cuenta que los punteros inteligentes, al ser construcciones programáticas, se pueden usar para hacer todo tipo de otras cosas interesantes, vea la respuesta de Dario, que están completamente fuera del alcance de la recolección de basura. Si desea hacer eso, necesitará punteros inteligentes.

Sin embargo, para fines de administración de memoria, no veo ninguna posibilidad de que los punteros inteligentes reemplacen la recolección de basura. Simplemente no son tan buenos en eso.

Tom Anderson
fuente
66
@Tom: mira la respuesta de Dario para obtener detalles sobre los punteros inteligentes. En cuanto a las ventajas de los punteros inteligentes, la desasignación determinista puede ser una enorme ventaja cuando se usa para controlar recursos (no solo memoria). De hecho, esto ha demostrado ser tan importante que Microsoft ha introducido el usingbloqueo en versiones posteriores de C #. Además, el comportamiento no determinista de los GC puede ser prohibitivo en los sistemas en tiempo real (razón por la cual los GC no se usan allí). Además, no olvidemos que los GC son tan complejos para acertar que la mayoría pierden memoria y son bastante ineficientes (por ejemplo, Boehm ...).
Konrad Rudolph
66
El no determinismo de los GC es, creo, una especie de arenque rojo: hay sistemas de GC que son adecuados para uso en tiempo real (como el Recycler de IBM), aunque los que se ven en las máquinas virtuales de escritorio y servidor no lo son. Además, usar punteros inteligentes significa usar malloc / free, y las implementaciones convencionales de malloc no son deterministas debido a la necesidad de buscar en la lista gratuita. Los sistemas de GC en movimiento tienen más tiempos de asignación deterministas que los sistemas malloc / free, aunque, por supuesto, menos tiempos de desasignación deterministas.
Tom Anderson
3
En cuanto a la complejidad: sí, los GC son complejos, pero no soy consciente de que "la mayoría pierden memoria y son bastante ineficientes", y estaría interesado en ver alguna evidencia de lo contrario. Boehm no es evidencia, porque es una implementación muy primitiva, y está diseñada para servir un lenguaje, C, donde la GC precisa es fundamentalmente imposible debido a la falta de seguridad de tipos. Es un esfuerzo valiente, y que funcione es muy impresionante, pero no puede tomarlo como un ejemplo de GC.
Tom Anderson
8
@ Jon: decididamente no es una mierda. bugzilla.novell.com/show_bug.cgi?id=621899 o, más generalmente: flyingfrogblog.blogspot.com/2009/01/… Esto es bien conocido y una propiedad de todos los GC conservadores.
Konrad Rudolph
3
"El costo de tiempo de ejecución de un GC moderno es menor que un sistema normal / libre de malloc". Arenque rojo aquí. Esto es solo porque el malloc tradicional es un algoritmo terriblemente ineficiente. Los asignadores modernos que usan múltiples cubos para diferentes tamaños de bloque son mucho más rápidos de asignar, mucho menos propensos a la fragmentación del montón y aún le brindan una rápida desasignación.
Mason Wheeler
3

El término recolección de basura implica que hay basura para recolectar. En C ++, los punteros inteligentes vienen en múltiples sabores, lo más importante es el unique_ptr. El unique_ptr es básicamente una construcción de propiedad y alcance único. En un código bien diseñado, la mayoría de las cosas asignadas al montón normalmente residen detrás de los punteros inteligentes unique_ptr y la propiedad de esos recursos estará bien definida en todo momento. Casi no hay sobrecarga en unique_ptr y unique_ptr elimina la mayoría de los problemas de administración de memoria manual que tradicionalmente conducían a las personas a los idiomas administrados. Ahora que más núcleos que se ejecutan simultáneamente se están volviendo más comunes, los principios de diseño que impulsan al código a usar una propiedad única y bien definida en cualquier momento se vuelven más importantes para el rendimiento.

Incluso en un programa bien diseñado, especialmente en entornos de subprocesos múltiples, no todo se puede expresar sin estructuras de datos compartidas, y para aquellas estructuras de datos que realmente requieren, los hilos deben comunicarse. RAII en c ++ funciona bastante bien para problemas de por vida en una configuración de un solo subproceso, en una configuración de subprocesos múltiples la vida útil de los objetos puede no estar completamente apilada jerárquicamente. Para estas situaciones, el uso de shared_ptr ofrece una gran parte de la solución. Usted crea la propiedad compartida de un recurso y esto en C ++ es el único lugar donde vemos basura, pero en cantidades tan pequeñas que un programa de C ++ diseñado adecuadamente debería considerarse más para implementar la recolección de 'basura' con ptr's compartidos que la recolección de basura completa como implementado en otros idiomas. C ++ simplemente no tiene tanta "basura"

Como han dicho otros, los punteros inteligentes contados por referencia son una forma de recolección de basura, y para eso tiene un problema importante. El ejemplo que se usa principalmente como inconveniente de las formas de recolección de basura contadas por referencia es el problema con la creación de estructuras de datos huérfanos conectadas entre sí con punteros inteligentes que crean grupos de objetos que evitan que se recopilen entre sí. Mientras que en un programa diseñado de acuerdo con el modelo de cómputo del actor, las estructuras de datos generalmente no permiten que surjan tales grupos no recopilables en C ++, cuando se usa el enfoque de datos compartidos amplios para la programación de subprocesos múltiples, como se usa predominantemente en gran parte de la industria, estos grupos huérfanos pueden convertirse rápidamente en realidad.

Entonces, para resumirlo todo, si por el uso de puntero compartido te refieres al uso amplio de unique_ptr combinado con el modelo de actor de enfoque de computación para programación multihilo y el uso limitado de shared_ptr, que otras formas de recolección de basura no te compran Beneficios añadidos. Sin embargo, si un enfoque de todo lo compartido le permitiera terminar con shared_ptr por todas partes, entonces debería considerar cambiar los modelos de simultaneidad o cambiar a un lenguaje administrado que esté más orientado hacia un mayor intercambio de propiedad y acceso concurrente a las estructuras de datos.

usuario1703394
fuente
1
¿Significa Rustque no necesita recolección de basura?
Gulshan
1
@Gulshan Rust es uno de los pocos idiomas que admite punteros únicos seguros.
CodesInChaos
2

La mayoría de los punteros inteligentes se implementan utilizando el recuento de referencias. Es decir, cada puntero inteligente que se refiere a un objeto incrementa el recuento de referencias de los objetos. Cuando ese recuento llega a cero, se libera el objeto.

El problema es si tienes referencias circulares. Es decir, A tiene una referencia a B, B tiene una referencia a C y C tiene una referencia a A. Si está utilizando punteros inteligentes, entonces para liberar la memoria asociada con A, B y C necesita manualmente obtener una "ruptura" de la referencia circular (p. ej., weak_ptren C ++).

La recolección de basura (típicamente) funciona de manera bastante diferente. La mayoría de los recolectores de basura en estos días utilizan una prueba de accesibilidad . Es decir, analiza todas las referencias en la pila y las que son accesibles globalmente y luego rastrea cada objeto al que hacen referencia esas referencias y los objetos a los que se refieren, etc. Todo lo demás es basura.

De ese modo, las referencias circulares ya no importan más - siempre y cuando ni A, B y C son accesibles , la memoria puede ser reclamado.

Hay otras ventajas en la recolección de basura "real". Por ejemplo, la asignación de memoria es extremadamente barata: simplemente incremente el puntero al "final" del bloque de memoria. La desasignación también tiene un costo amortizado constante. Pero, por supuesto, los lenguajes como C ++ le permiten implementar la administración de memoria de la forma que desee, por lo que podría idear una estrategia de asignación que sea aún más rápida.

Por supuesto, en C ++, la cantidad de memoria asignada en el montón suele ser menor que un lenguaje de referencia como C # /. NET. Pero eso no es realmente un problema de recolección de basura versus punteros inteligentes.

En cualquier caso, el problema no es cortar y secar, uno es mejor que el otro. Cada uno tiene ventajas y desventajas.

Dean Harding
fuente
2

Se trata de rendimiento . La asignación de memoria requiere mucha administración. Si la asignación se ejecuta en segundo plano, aumenta el rendimiento del proceso en primer plano. Desafortunadamente, la asignación de memoria no puede ser perezosa (los objetos asignados se usarán en el momento sagrado siguiente), pero la liberación de objetos sí.

Pruebe en C ++ (sin GC) para asignar un gran grupo de objetos, imprima "hola" y luego bórrelos. Te sorprenderá cuánto tiempo lleva liberar objetos.

Además, GNU libc proporciona herramientas más efectivas para la asignación de memoria, vea los obstáculos . Debo notar que no tengo experiencia con obstáculos, nunca los usé.

ern0
fuente
En principio, tiene un punto, pero debe tenerse en cuenta que este es un problema que tiene una solución muy simple: use un asignador de agrupación o un asignador de objetos pequeños para agrupar las asignaciones de negocios. Pero esto ciertamente requiere (un poco) más esfuerzo que tener un GC ejecutado en segundo plano.
Konrad Rudolph
Sí, claro, GC es mucho más cómodo. (Especialmente para principiantes: no hay problemas de propiedad, ni siquiera hay un operador de eliminación.)
ern0
3
@ ern0: no. El punto principal de los punteros inteligentes (recuento de referencias) es que no hay ningún problema de propiedad ni operador de eliminación.
Konrad Rudolph
3
@ Jon: que, sinceramente, es la mayor parte del tiempo. Si comparte el estado del objeto entre hilos diferentes, tendrá problemas completamente diferentes. Admito que muchas personas programan de esa manera, pero esto es una consecuencia de las malas abstracciones de subprocesos que han existido hasta hace poco y no es una buena manera de hacer subprocesos múltiples.
Konrad Rudolph
1
La desasignación a menudo no se realiza "en segundo plano", sino que detiene todos los hilos de primer plano. La recolección de basura en modo lote generalmente es una ganancia de rendimiento, a pesar de la pausa de los hilos de primer plano, porque permite consolidar el espacio no utilizado. Se podrían separar los procesos de recolección de basura y la compactación del montón, pero, especialmente en marcos que usan referencias directas en lugar de identificadores, ambos tienden a ser procesos de "parada del mundo", y a menudo es más práctico hacerlo. juntos.
supercat
2

La recolección de basura puede ser más eficiente: básicamente 'agrupa' la sobrecarga de la administración de memoria y lo hace todo a la vez. En general, esto dará como resultado que se gaste menos CPU en general en la desasignación de memoria, pero significa que tendrá un gran estallido de actividad de desasignación en algún momento. Si el GC no está diseñado correctamente, el usuario puede verlo como una 'pausa' mientras el GC intenta desasignar la memoria. La mayoría de los GC modernos son muy buenos para mantener esto invisible para el usuario, excepto en las condiciones más adversas.

Los punteros inteligentes (o cualquier esquema de conteo de referencias) tienen la ventaja de que suceden exactamente cuando se espera al mirar el código (el puntero inteligente se sale del alcance, la cosa se elimina). Obtienes pequeñas explosiones de desasignación aquí y allá. En general, puede usar más tiempo de CPU en la desasignación, pero dado que se extiende sobre todas las cosas que suceden en su programa, es menos probable (excluyendo la desasignación de alguna estructura de datos de monstruos) que sea visible para su usuario.

Si está haciendo algo donde la capacidad de respuesta es importante, le sugiero que los punteros inteligentes / el conteo de referencias le permitan saber exactamente cuándo están sucediendo las cosas, para que pueda saber mientras codifica lo que probablemente será visible para sus usuarios. En una configuración de GC solo tiene el control más efímero sobre el recolector de basura y simplemente tiene que intentar solucionar el problema.

Por otro lado, si su rendimiento general es su objetivo, un sistema basado en GC puede ser una opción mucho mejor, ya que minimiza los recursos necesarios para administrar la memoria.

Ciclos: no considero que el problema de los ciclos sea significativo. En un sistema en el que tiene punteros inteligentes, tiende a las estructuras de datos que no tienen ciclos, o simplemente tiene cuidado con cómo dejar ir esas cosas. Si es necesario, se pueden usar objetos de mantenimiento que sepan cómo romper los ciclos en los objetos de propiedad para asegurar automáticamente la destrucción adecuada. En algunos ámbitos de la programación, esto puede ser importante, pero para la mayoría del trabajo diario es irrelevante.

Michael Kohne
fuente
1
"Tendrá una gran explosión de actividad de desasignación en algún momento". La cinta de correr de Baker es un ejemplo de un recolector de basura increíblemente incremental. memorymanagement.org/glossary/t.html#treadmill
Jon Harrop
1

La limitación número uno de los punteros inteligentes es que no siempre ayudan contra referencias circulares. Por ejemplo, tiene el objeto A que almacena un puntero inteligente al objeto B y el objeto B está almacenando un puntero inteligente al objeto A. Si se dejan juntos sin restablecer ninguno de los punteros, nunca se desasignarán.

Esto sucede porque un puntero inteligente tiene que realizar una acción específica que no será triigered en el escenario anterior porque ambos objetos son inalcanzables para el programa. La recolección de basura se las arreglará: identificará adecuadamente que los objetos no son alcanzables para el programa y serán recolectados.

diente filoso
fuente
1

Es un espectro .

Si no tiene límites estrictos en cuanto al rendimiento y está preparado para trabajar duro, terminará en la asamblea o c, con toda la responsabilidad de tomar las decisiones correctas y toda la libertad para hacerlo, pero con eso , toda la libertad para estropearlo:

"Te diré qué hacer, hazlo. Confía en mí".

La recolección de basura es el otro extremo del espectro. Usted tiene muy poco control, pero se encarga de usted:

"Te diré lo que quiero, lo haces realidad".

Esto tiene muchas ventajas, sobre todo porque no es necesario ser tan confiable cuando se trata de saber exactamente cuándo un recurso ya no es necesario, pero (a pesar de algunas de las respuestas que circulan por aquí) no es bueno para el rendimiento, y La previsibilidad del rendimiento. (Como todas las cosas, si se le da el control y hace algo estúpido, puede tener peores resultados. Sin embargo, sugerir que saber en tiempo de compilación cuáles son las condiciones para poder liberar memoria, no se puede usar como una ganancia de rendimiento es más que ingenuo).

RAII, alcance, recuento de referencias, etc. son todos ayudantes para permitirle avanzar más a lo largo de ese espectro, pero no todo el camino. Todas estas cosas aún requieren un uso activo. Todavía permiten y requieren que interactúe con la administración de memoria de una manera que la recolección de basura no lo hace.

drjpizzle
fuente
0

Recuerde que al final, todo se reduce a una CPU que ejecuta las instrucciones. Que yo sepa, todas las CPU de grado de consumidor tienen conjuntos de instrucciones que requieren que tenga datos almacenados en un lugar determinado en la memoria y que tenga punteros a dichos datos. Eso es todo lo que tienes en el nivel básico.

Todo esto además de la recolección de basura, las referencias a los datos que pueden haberse movido, la compactación del montón, etc., etc. está haciendo el trabajo dentro de las restricciones dadas por el paradigma anterior "fragmento de memoria con un puntero de dirección". Lo mismo con los punteros inteligentes: TODAVÍA tiene que hacer que el código se ejecute en hardware real.


fuente