No estoy haciendo esta pregunta debido a los méritos de la recolección de basura en primer lugar. Mi razón principal para preguntar esto es que sé que Bjarne Stroustrup ha dicho que C ++ tendrá un recolector de basura en algún momento.
Dicho esto, ¿por qué no se ha agregado? Ya hay algunos recolectores de basura para C ++. ¿Es esta una de esas cosas del tipo "más fácil decirlo que hacerlo"? ¿O hay otras razones por las que no se ha agregado (y no se agregará en C ++ 11)?
Enlaces cruzados:
Solo para aclarar, entiendo las razones por las que C ++ no tenía un recolector de basura cuando se creó por primera vez. Me pregunto por qué no se puede agregar el colector.
c++
garbage-collection
c++11
Jason Baker
fuente
fuente
Respuestas:
La recolección de basura implícita podría haberse agregado, pero simplemente no fue suficiente. Probablemente no solo por complicaciones de implementación, sino también porque las personas no pueden llegar a un consenso general lo suficientemente rápido.
Una cita del propio Bjarne Stroustrup:
Hay una buena discusión sobre el tema aquí .
Visión general:
C ++ es muy poderoso y te permite hacer casi cualquier cosa. Por esta razón, no le empuja automáticamente muchas cosas que podrían afectar el rendimiento. La recolección de basura se puede implementar fácilmente con punteros inteligentes (objetos que envuelven punteros con un recuento de referencia, que se eliminan automáticamente cuando el recuento de referencia llega a 0).
C ++ fue construido con competidores en mente que no tenían recolección de basura. La eficiencia fue la principal preocupación de que C ++ tuvo que defenderse de las críticas en comparación con C y otros.
Hay 2 tipos de recolección de basura ...
Recolección de basura explícita:
C ++ 0x tendrá recolección de basura mediante punteros creados con shared_ptr
Si lo desea, puede usarlo, si no lo quiere, no está obligado a usarlo.
Actualmente, también puedes usar boost: shared_ptr si no quieres esperar a C ++ 0x.
Recolección de basura implícita:
Sin embargo, no tiene recolección de basura transparente. Sin embargo, será un punto de enfoque para futuras especificaciones de C ++.
¿Por qué Tr1 no tiene recolección de basura implícita?
Hay muchas cosas que debería haber tenido tr1 de C ++ 0x, Bjarne Stroustrup en entrevistas anteriores declaró que tr1 no tenía tanto como le hubiera gustado.
fuente
smart_ptr's
? ¿Cómo haría una bifurcación de estilo Unix de bajo nivel, con un recolector de basura en el camino? Otras cosas se verían afectadas, como el enhebrado. Python tiene su bloqueo de intérprete global principalmente debido a su recolección de basura (ver Cython). Manténgalo fuera de C / C ++, gracias.std::shared_ptr
) son las referencias cíclicas, que causan una pérdida de memoria. Por lo tanto, debe usarstd::weak_ptr
con cuidado para romper los ciclos, lo cual es desordenado. El estilo de marca y barrido GC no tiene este problema. No hay incompatibilidad inherente entre subprocesos / bifurcaciones y recolección de basura. Java y C # tienen subprocesamiento múltiple preventivo de alto rendimiento y un recolector de basura. Hay problemas que hacer con las aplicaciones en tiempo real y un recolector de basura, ya que la mayoría de los recolectores de basura tienen que detener el mundo para funcionar.std::shared_ptr
) son las referencias cíclicas" y el rendimiento horrible, lo cual es irónico porque un mejor rendimiento suele ser la justificación para usar C ++ ... flyingfrogblog.blogspot.co.uk/2011/01/…Para agregar al debate aquí.
Existen problemas conocidos con la recolección de basura, y comprenderlos ayuda a comprender por qué no hay ninguno en C ++.
1. ¿Rendimiento?
La primera queja es a menudo sobre el rendimiento, pero la mayoría de las personas realmente no se dan cuenta de lo que están hablando. Como lo ilustra
Martin Beckett
el problema, puede no ser el rendimiento per se, sino la previsibilidad del rendimiento.Actualmente hay 2 familias de GC que se implementan ampliamente:
El
Mark And Sweep
es más rápido (menos impacto en el rendimiento general) pero sufre de un síndrome de "congelar el mundo": es decir, cuando se activa el GC, todo lo demás se detiene hasta que el GC haya realizado su limpieza. Si desea crear un servidor que responda en unos pocos milisegundos ... algunas transacciones no cumplirán con sus expectativas :)El problema
Reference Counting
es diferente: el recuento de referencias agrega una sobrecarga, especialmente en entornos de subprocesos múltiples porque necesita tener un conteo atómico. Además, existe el problema de los ciclos de referencia, por lo que necesita un algoritmo inteligente para detectar esos ciclos y eliminarlos (generalmente también se implementa mediante un "congelamiento del mundo", aunque menos frecuente). En general, a partir de hoy, este tipo (aunque normalmente responde mejor o se congela con menos frecuencia) es más lento que elMark And Sweep
.He visto un artículo de los implementadores de Eiffel que intentaban implementar un
Reference Counting
recolector de basura que tendría un rendimiento global similar alMark And Sweep
del aspecto "Congelar el mundo". Se requiere un hilo separado para el GC (típico). El algoritmo fue un poco aterrador (al final), pero el trabajo hizo un buen trabajo al introducir los conceptos uno por uno y mostrar la evolución del algoritmo desde la versión "simple" a la versión completa. Lectura recomendada si solo pudiera volver a poner mis manos sobre el archivo PDF ...2. La adquisición de recursos es la inicialización (RAII)
Es un idioma común en el sentido de
C++
que envolverá la propiedad de los recursos dentro de un objeto para asegurarse de que se liberen correctamente. Se usa principalmente para la memoria ya que no tenemos recolección de basura, pero también es útil para muchas otras situaciones:La idea es controlar adecuadamente la vida útil del objeto:
El problema de GC es que si ayuda con el primero y, en última instancia, garantiza que más tarde ... este "último" puede no ser suficiente. Si liberas un bloqueo, ¡realmente te gustaría que se libere ahora, para que no bloquee más llamadas!
Los idiomas con GC tienen dos soluciones:
using
construir ... pero es RAII explícito (débil) mientras que en C ++ RAII está implícito para que el usuario NO PUEDA cometer el error sin darse cuenta (al omitir lausing
palabra clave)3. Punteros inteligentes
Los punteros inteligentes a menudo aparecen como una bala de plata para manejar la memoria
C++
. Muchas veces he escuchado: no necesitamos GC después de todo, ya que tenemos punteros inteligentes.Uno no podría estar más equivocado.
Los punteros inteligentes ayudan:
auto_ptr
yunique_ptr
usan conceptos RAII, extremadamente útiles. Son tan simples que puede escribirlos usted mismo con bastante facilidad.Sin embargo, cuando se necesita compartir la propiedad, se vuelve más difícil: puede compartir entre varios subprocesos y hay algunos problemas sutiles con el manejo del recuento. Por lo tanto, uno naturalmente va hacia
shared_ptr
.Es genial, para eso es Boost después de todo, pero no es una bala de plata. De hecho, el problema principal
shared_ptr
es que emula un GC implementado por,Reference Counting
pero debe implementar la detección del ciclo usted mismo ... UrgPor supuesto, existe esta
weak_ptr
cosita, pero desafortunadamente ya he visto pérdidas de memoria a pesar del usoshared_ptr
debido a esos ciclos ... y cuando estás en un entorno de múltiples subprocesos, ¡es extremadamente difícil de detectar!4. ¿Cuál es la solución?
No hay una bala de plata, pero como siempre, definitivamente es factible. En ausencia de GC, uno debe ser claro sobre la propiedad:
weak_ptr
De hecho, sería genial tener un GC ... sin embargo, no es un problema trivial. Y mientras tanto, solo tenemos que arremangarnos.
fuente
¿Que tipo? ¿Debería estar optimizado para controladores de lavadora integrados, teléfonos celulares, estaciones de trabajo o supercomputadoras?
¿Debería priorizar la capacidad de respuesta de la interfaz gráfica de usuario o la carga del servidor?
¿debería usar mucha memoria o mucha CPU?
C / c ++ se usa en demasiadas circunstancias diferentes. Sospecho que algo como impulsar los punteros inteligentes será suficiente para la mayoría de los usuarios
Editar: los recolectores de basura automáticos no son tanto un problema de rendimiento (siempre se puede comprar más servidor) es una cuestión de rendimiento predecible.
No saber cuándo va a entrar en funcionamiento el GC es como emplear a un piloto de aerolínea narcoléptico, la mayoría de las veces son geniales, ¡pero cuando realmente necesita respuesta!
fuente
Una de las principales razones por las que C ++ no ha incorporado la recolección de basura es que hacer que la recolección de basura juegue bien con los destructores es muy, muy difícil. Hasta donde yo sé, nadie realmente sabe cómo resolverlo completamente todavía. Hay muchos problemas con los que lidiar:
Estos son solo algunos de los problemas enfrentados.
fuente
Dispose
sobre un objeto puede hacer que sea unsable, pero las referencias que apuntaban al objeto cuando estaba vivo continuará haciéndolo después de que esté muerto. Por el contrario, en los sistemas que no son GC, los objetos se pueden eliminar mientras existan referencias, y rara vez hay algún límite para los estragos que pueden generarse si se utiliza una de esas referencias.Aunque esta es una vieja pregunta, todavía hay un problema que no veo que nadie haya abordado en absoluto: la recolección de basura es casi imposible de especificar.
En particular, el estándar C ++ es bastante cuidadoso al especificar el lenguaje en términos de comportamiento externo observable, en lugar de cómo la implementación logra ese comportamiento. En el caso de la recolección de basura, sin embargo, no es virtualmente ningún comportamiento observable externamente.
La idea general de la recolección de basura es que debe hacer un intento razonable de asegurar que una asignación de memoria tenga éxito. Desafortunadamente, es esencialmente imposible garantizar que cualquier asignación de memoria tenga éxito, incluso si tiene un recolector de basura en funcionamiento. Esto es cierto hasta cierto punto en cualquier caso, pero particularmente en el caso de C ++, porque (probablemente) no es posible utilizar un recopilador de copia (o algo similar) que mueva objetos en la memoria durante un ciclo de recopilación.
Si no puede mover objetos, no puede crear un espacio de memoria único y contiguo desde el cual hacer sus asignaciones, y eso significa que su montón (o almacén gratuito, o como prefiera llamarlo) puede, y probablemente lo hará , fragmentarse con el tiempo. Esto, a su vez, puede evitar que una asignación tenga éxito, incluso cuando hay más memoria libre que la cantidad solicitada.
Si bien es posible encontrar alguna garantía que diga (en esencia) que si repite exactamente el mismo patrón de asignación repetidamente, y tuvo éxito la primera vez, continuará teniendo éxito en las iteraciones posteriores, siempre que la memoria asignada se volvió inaccesible entre iteraciones. Esa es una garantía tan débil que es esencialmente inútil, pero no veo ninguna esperanza razonable de fortalecerla.
Aun así, es más fuerte de lo que se ha propuesto para C ++. La propuesta anterior [advertencia: PDF] (que se descartó) no garantizaba nada en absoluto. En 28 páginas de propuesta, lo que obtuvo en el camino del comportamiento observable externamente fue una nota única (no normativa) que decía:
Al menos para mí, esto plantea una pregunta seria sobre el retorno de la inversión. Vamos a romper el código existente (nadie sabe exactamente cuánto, pero definitivamente un poco), establecer nuevos requisitos en las implementaciones y nuevas restricciones en el código, y lo que obtenemos a cambio es, posiblemente, ¿nada?
Incluso en el mejor de los casos, lo que obtenemos son programas que, basados en pruebas con Java , probablemente requerirán alrededor de seis veces más memoria para ejecutarse a la misma velocidad que lo hacen ahora. Peor aún, la recolección de basura formó parte de Java desde el principio: C ++ impone suficientes restricciones en el recolector de basura que seguramente tendrá una relación costo / beneficio aún peor (incluso si vamos más allá de lo que garantiza la propuesta y suponemos que habrá algún beneficio).
Resumiría la situación matemáticamente: esta es una situación compleja. Como cualquier matemático sabe, un número complejo tiene dos partes: real e imaginaria. Me parece que lo que tenemos aquí son costos reales, pero beneficios que son (al menos en su mayoría) imaginarios.
fuente
free
(donde quiero decirfree
análogo al lenguaje C). Pero Java nunca garantiza llamar a finalizadores ni nada por el estilo. De hecho, C ++ hace mucho más que Java para ejecutar escrituras de base de datos de confirmación, enjuagar identificadores de archivos, etc. Java afirma tener "GC", pero los desarrolladores de Java deben llamar meticulosamenteclose()
todo el tiempo y deben ser muy conscientes de la gestión de recursos, teniendo cuidado de no llamarclose()
demasiado pronto o demasiado tarde. C ++ nos libera de eso. ... (continuación)try (Whatever w=...) {...}
lo que lo resuelve (y recibirá una advertencia cuando lo olvide). Los restantes también son problemáticos con RAII. Llamarclose()
"todo el tiempo" significa tal vez una vez por decenas de miles de líneas, por lo que no es tan malo, mientras que la memoria se asigna casi en cada línea de Java.Fuente: http://www.stroustrup.com/bs_faq.html#garbage-collection
En cuanto a por qué no lo tiene incorporado, si recuerdo correctamente, fue inventado antes de que GC fuera la cosa , y no creo que el lenguaje pudiera haber tenido GC por varias razones (compatibilidad con IE con versiones anteriores de C)
Espero que esto ayude.
fuente
Stroustrup hizo algunos buenos comentarios al respecto en la conferencia Going Native 2013.
Simplemente pase a unos 25m50s en este video . (Recomiendo ver el video completo en realidad, pero esto salta a las cosas sobre la recolección de basura).
Cuando tiene un lenguaje realmente excelente que hace que sea fácil (y seguro, predecible, fácil de leer y fácil de enseñar) manejar objetos y valores de forma directa, evitando el uso (explícito) del montón, entonces ni siquiera quieres la recolección de basura.
Con C ++ moderno, y las cosas que tenemos en C ++ 11, la recolección de basura ya no es deseable, excepto en circunstancias limitadas. De hecho, incluso si un buen recolector de basura está integrado en uno de los principales compiladores de C ++, creo que no se usará con mucha frecuencia. Será más fácil , no más difícil, evitar el GC.
Él muestra este ejemplo:
Esto no es seguro en C ++. ¡Pero tampoco es seguro en Java! En C ++, si la función regresa temprano,
delete
nunca se llamará. Pero si tuvo una recolección de basura completa, como en Java, simplemente recibe una sugerencia de que el objeto se destruirá "en algún momento en el futuro" ( Actualización: es aún peor que esto. Java nopromete llamar al finalizador alguna vez, tal vez nunca se llame). Esto no es lo suficientemente bueno si Gadget tiene un identificador de archivo abierto, o una conexión a una base de datos, o datos que ha guardado para escribir en una base de datos en un momento posterior. Queremos que el Gadget se destruya tan pronto como esté terminado, para liberar estos recursos lo antes posible. No desea que su servidor de bases de datos tenga problemas con miles de conexiones de bases de datos que ya no se necesitan, no sabe que su programa ha terminado de funcionar.Entonces, ¿cuál es la solución? Hay algunos enfoques. El enfoque obvio, que utilizará para la gran mayoría de sus objetos es:
Esto requiere menos caracteres para escribir. No tiene que
new
interponerse en el camino. No requiere que escribaGadget
dos veces. El objeto se destruye al final de la función. Si esto es lo que quieres, esto es muy intuitivo.Gadget
s se comportan igual queint
odouble
. Predecible, fácil de leer, fácil de enseñar. Todo es un "valor". A veces es un gran valor, pero los valores son más fáciles de enseñar porque no tienes esta cosa de 'acción a distancia' que obtienes con punteros (o referencias).La mayoría de los objetos que crea son para usar solo en la función que los creó, y tal vez pasen como entradas a las funciones secundarias. El programador no debería tener que pensar en la "administración de memoria" al devolver objetos, o de otra manera compartir objetos en partes muy separadas del software.
El alcance y la vida útil son importantes. La mayoría de las veces, es más fácil si la vida útil es la misma que el alcance. Es más fácil de entender y más fácil de enseñar. Cuando desee una vida útil diferente, debería ser obvio leer el código que está haciendo esto, por
shared_ptr
ejemplo , utilizando . (O devolviendo objetos (grandes) por valor, aprovechando la semántica de movimiento ounique_ptr
.Esto puede parecer un problema de eficiencia. ¿Qué sucede si quiero devolver un gadget
foo()
? La semántica de movimiento de C ++ 11 facilita la devolución de objetos grandes. Simplemente escribaGadget foo() { ... }
y simplemente funcionará, y funcionará rápidamente. No necesita meterse consigo&&
mismo, simplemente devuelva las cosas por valor y el lenguaje a menudo podrá hacer las optimizaciones necesarias. (Incluso antes de C ++ 03, los compiladores hicieron un trabajo notablemente bueno al evitar copias innecesarias).Como dijo Stroustrup en otra parte del video (parafraseando): "Solo un informático insistiría en copiar un objeto y luego destruir el original. (La audiencia se ríe). ¿Por qué no mover el objeto directamente a la nueva ubicación? Esto es lo que los humanos (no los informáticos) esperan ".
Cuando puede garantizar que solo se necesita una copia de un objeto, es mucho más fácil comprender la vida útil del objeto. Puede elegir qué política de por vida desea, y la recolección de basura está ahí si lo desea. Pero cuando comprenda los beneficios de los otros enfoques, encontrará que la recolección de basura se encuentra al final de su lista de preferencias.
Si eso no funciona para usted, usted puede utilizar
unique_ptr
, o en su defecto,shared_ptr
. Bien escrito C ++ 11 es más corto, más fácil de leer y más fácil de enseñar que muchos otros lenguajes cuando se trata de la gestión de la memoria.fuente
Gadget
no pide nada más para hacer algo en su nombre, el código original estaría perfectamente seguro en Java sidelete
se eliminara la declaración sin sentido (para Java) .shared_ptr<T>
especialmente cuandoT
es "aburrido". Podría decidir no administrar realmente un contador de referencia para ese tipo y, en su lugar, usar GC. Esto permitiría que GC se use sin que el desarrollador tenga que darse cuenta. Ashared_ptr
podría verse simplemente como un puntero GC, por adecuadoT
. Pero hay limitaciones en esto, y haría que muchos programas fueran más lentos.string1=string2;
se ejecutará muy rápidamente independientemente de la longitud de la cadena (literalmente no es más que una carga de registro y un almacén de registros), y no requiere ningún bloqueo para garantizar que si la instrucción anterior se ejecuta mientrasstring2
está siendo escrito,string1
retendrá el valor antiguo o el nuevo valor, sin Comportamiento Indefinido).shared_ptr<String>
requiere mucha sincronización detrás de escena, y la asignación de aString
puede comportarse de manera extraña si una variable se lee y se escribe simultáneamente. Los casos en los que uno quisiera escribir y leerString
simultáneamente no son terriblemente comunes, pero pueden surgir si, por ejemplo, algún código desea poner a disposición de otros hilos informes de estado continuos. En .NET y Java, tales cosas simplemente "funcionan".Porque el C ++ moderno no necesita recolección de basura.
La respuesta a las preguntas frecuentes de Bjarne Stroustrup sobre este asunto dice :
La situación, para el código escrito en estos días (C ++ 17 y siguiendo las Pautas básicas oficiales ) es la siguiente:
"¿Ah sí? Pero ¿qué pasa con ...
... si solo escribo código de la forma en que solíamos escribir C ++ en los viejos tiempos? "
De hecho, podría ignorar todas las pautas y escribir código de aplicación con fugas, y se compilará y ejecutará (y se filtrará), como siempre.
Pero no es una situación de "simplemente no hagas eso", donde se espera que el desarrollador sea virtuoso y ejerza mucho autocontrol; simplemente no es más simple escribir código no conforme, ni es más rápido de escribir, ni tiene mejor rendimiento. Gradualmente, también se volverá más difícil de escribir, ya que se enfrentaría a un "desajuste de impedancia" cada vez mayor con lo que el código conforme proporciona y espera.
... si yo
reintrepret_cast
? O hacer aritmética puntero complejo? ¿O otros trucos similares?De hecho, si te lo propones, puedes escribir código que arruine las cosas a pesar de jugar bien con las pautas. Pero:
... desarrollo de la biblioteca?
Si usted es un desarrollador de bibliotecas C ++, entonces escribe código inseguro que involucra punteros sin procesar, y debe codificar con cuidado y responsabilidad, pero estos son fragmentos de código independientes escritos por expertos (y lo más importante, revisados por expertos).
Entonces, es como dijo Bjarne: en general, no hay motivación para recolectar basura, ya que todos se aseguran de no producir basura. GC se está convirtiendo en un problema con C ++.
Eso no quiere decir que GC no sea un problema interesante para ciertas aplicaciones específicas, cuando se desea emplear estrategias de asignación y desasignación personalizadas. Para aquellos que deseen asignación personalizada y desasignación, no un GC a nivel de idioma.
fuente
La idea detrás de C ++ era que no pagaría ningún impacto en el rendimiento de las funciones que no utiliza. Por lo tanto, agregar la recolección de basura habría significado que algunos programas se ejecuten directamente en el hardware como lo hace C y algunos dentro de algún tipo de máquina virtual en tiempo de ejecución.
Nada le impide utilizar algún tipo de punteros inteligentes que están vinculados a algún mecanismo de recolección de basura de terceros. Me parece recordar que Microsoft hizo algo así con COM y no funcionó bien.
fuente
Para responder la mayoría de las preguntas "por qué" sobre C ++, lea Diseño y evolución de C ++
fuente
Uno de los principios fundamentales detrás del lenguaje C original es que la memoria está compuesta de una secuencia de bytes, y el código solo necesita preocuparse por lo que esos bytes significan en el momento exacto en que se están utilizando. Modern C permite a los compiladores imponer restricciones adicionales, pero C incluye, y C ++ conserva, la capacidad de descomponer un puntero en una secuencia de bytes, ensamblar cualquier secuencia de bytes que contengan los mismos valores en un puntero y luego usar ese puntero para acceder al objeto anterior.
Si bien esa capacidad puede ser útil, o incluso indispensable, en algunos tipos de aplicaciones, un lenguaje que incluya esa capacidad será muy limitado en su capacidad para admitir cualquier tipo de recolección de basura útil y confiable. Si un compilador no sabe todo lo que se ha hecho con los bits que formaron un puntero, no tendrá forma de saber si podría existir información suficiente para reconstruir el puntero en algún lugar del universo. Dado que sería posible almacenar esa información de manera que la computadora no pudiera acceder incluso si supiera de ellos (por ejemplo, los bytes que componen el puntero podrían haberse mostrado en la pantalla el tiempo suficiente para que alguien escriba en una hoja de papel), puede ser literalmente imposible para una computadora saber si un puntero podría usarse en el futuro.
Una peculiaridad interesante de muchos marcos de recolección de basura es que una referencia de objeto no está definida por los patrones de bits contenidos en ella, sino por la relación entre los bits contenidos en la referencia de objeto y otra información contenida en otro lugar. En C y C ++, si el patrón de bits almacenado en un puntero identifica un objeto, ese patrón de bits identificará ese objeto hasta que el objeto se destruya explícitamente. En un sistema GC típico, un objeto puede estar representado por un patrón de bits 0x1234ABCD en un momento dado, pero el siguiente ciclo GC puede reemplazar todas las referencias a 0x1234ABCD con referencias a 0x4321BABE, con lo cual el objeto estaría representado por el último patrón. Incluso si uno mostrara el patrón de bits asociado con una referencia de objeto y luego lo leyera desde el teclado,
fuente
Toda la conversación técnica está complicando demasiado el concepto.
Si coloca GC en C ++ para toda la memoria automáticamente, considere algo como un navegador web. El navegador web debe cargar un documento web completo Y ejecutar scripts web. Puede almacenar variables de script web en el árbol de documentos. En un documento GRANDE en un navegador con muchas pestañas abiertas, significa que cada vez que el GC debe hacer una colección completa, también debe escanear todos los elementos del documento.
En la mayoría de las computadoras esto significa que ocurrirán FALLAS DE PÁGINA. Entonces, la razón principal para responder la pregunta es que ocurrirán FALLAS DE PÁGINA. Lo sabrá cuando su PC comience a hacer mucho acceso al disco. Esto se debe a que el GC debe tocar mucha memoria para probar punteros no válidos. Cuando tiene una aplicación de buena fe que utiliza mucha memoria, tener que escanear todos los objetos de cada colección es un caos debido a los FALLOS DE PÁGINA. Un error de página es cuando la memoria virtual necesita volver a leerse en la RAM desde el disco.
Entonces, la solución correcta es dividir una aplicación en las partes que necesitan GC y las partes que no. En el caso del ejemplo del navegador web anterior, si el árbol de documentos se asignó con malloc, pero el JavaScript se ejecutó con GC, cada vez que el GC se activa solo escanea una pequeña porción de memoria y todos los elementos de la memoria PAGADOS. no es necesario volver a paginar el árbol de documentos.
Para comprender mejor este problema, busque en la memoria virtual y cómo se implementa en las computadoras. Se trata del hecho de que 2GB están disponibles para el programa cuando en realidad no hay tanta RAM. En las computadoras modernas con 2GB de RAM para un sistema de 32Bt, no es un problema, siempre que se esté ejecutando un solo programa.
Como ejemplo adicional, considere una colección completa que debe rastrear todos los objetos. Primero debe escanear todos los objetos accesibles a través de las raíces. Segundo escanee todos los objetos visibles en el paso 1. Luego escanee los destructores en espera. Luego, vaya a todas las páginas nuevamente y apague todos los objetos invisibles. Esto significa que muchas páginas pueden intercambiarse y volverse varias veces.
Entonces, mi respuesta para resumir es que el número de FALLAS DE PÁGINA que ocurren como resultado de tocar toda la memoria hace que el GC completo para todos los objetos en un programa sea inviable y, por lo tanto, el programador debe ver el GC como una ayuda para cosas como scripts y el trabajo de la base de datos, pero haga cosas normales con la gestión manual de la memoria.
Y la otra razón muy importante, por supuesto, son las variables globales. Para que el recopilador sepa que hay un puntero de variable global en el GC, necesitaría palabras clave específicas y, por lo tanto, el código C ++ existente no funcionaría.
fuente
RESPUESTA CORTA: No sabemos cómo recolectar basura de manera eficiente (con poco tiempo y sobrecarga de espacio) y correctamente todo el tiempo (en todos los casos posibles).
RESPUESTA LARGA: Al igual que C, C ++ es un lenguaje de sistemas; esto significa que se usa cuando está escribiendo código del sistema, por ejemplo, sistema operativo. En otras palabras, C ++ está diseñado, al igual que C, con el mejor rendimiento posible como objetivo principal. El lenguaje estándar no agregará ninguna característica que pueda obstaculizar el objetivo de rendimiento.
Esto detiene la pregunta: ¿por qué la recolección de basura dificulta el rendimiento? La razón principal es que, cuando se trata de implementación, nosotros [los informáticos] no sabemos cómo recolectar basura con una sobrecarga mínima, en todos los casos. Por lo tanto, es imposible para el compilador de C ++ y el sistema de tiempo de ejecución realizar una recolección de basura de manera eficiente todo el tiempo. Por otro lado, un programador de C ++, debe conocer su diseño / implementación y es la mejor persona para decidir cómo hacer la mejor recolección de basura.
Por último, si el control (hardware, detalles, etc.) y el rendimiento (tiempo, espacio, potencia, etc.) no son las restricciones principales, entonces C ++ no es la herramienta de escritura. Otro lenguaje podría servir mejor y ofrecer más administración de tiempo de ejecución [oculto], con la sobrecarga necesaria.
fuente
Cuando comparamos C ++ con Java, vemos que C ++ no fue diseñado teniendo en cuenta la recolección implícita de basura, mientras que Java sí.
Tener cosas como punteros arbitrarios en C-Style no solo es malo para las implementaciones de GC, sino que también destruiría la compatibilidad con versiones anteriores para una gran cantidad de C ++: código heredado.
Además de eso, C ++ es un lenguaje destinado a ejecutarse como ejecutable independiente en lugar de tener un entorno complejo de tiempo de ejecución.
Con todo: Sí, podría ser posible agregar Garbage Collection a C ++, pero en aras de la continuidad, es mejor no hacerlo.
fuente
Principalmente por dos razones:
C ++ ya ofrece administración manual de memoria, asignación de pila, RAII, contenedores, punteros automáticos, punteros inteligentes ... Eso debería ser suficiente. Los recolectores de basura son para programadores perezosos que no quieren pasar 5 minutos pensando en quién debería poseer qué objetos o cuándo deberían liberarse los recursos. Así no es como hacemos las cosas en C ++.
fuente
Imponer la recolección de basura es realmente un cambio de paradigma de bajo nivel a alto nivel.
Si observa la forma en que las cadenas se manejan en un lenguaje con recolección de basura, encontrará que SOLO permiten funciones de manipulación de cadenas de alto nivel y no permiten el acceso binario a las cadenas. En pocas palabras, todas las funciones de cadena primero verifican los punteros para ver dónde está la cadena, incluso si solo está extrayendo un byte. Entonces, si está haciendo un ciclo que procesa cada byte en una cadena en un idioma con recolección de basura, debe calcular la ubicación base más el desplazamiento para cada iteración, porque no puede saber cuándo se movió la cadena. Luego tienes que pensar en montones, pilas, hilos, etc.
fuente