¿Por qué los lenguajes como C y C ++ no tienen recolección de basura, mientras que Java sí? [cerrado]

57

Bueno, sé que hay cosas como malloc / free para C y new / using-a-destructor para la administración de memoria en C ++, pero me preguntaba por qué no hay "nuevas actualizaciones" para estos lenguajes que permitan al usuario ¿tiene la opción de administrar la memoria manualmente o que el sistema lo haga automáticamente (recolección de basura)?

Una pregunta algo novedosa, pero solo he estado en CS durante aproximadamente un año.

Templario oscuro
fuente
99
Tenemos un módulo en desarrollo de iPhone este semestre. Después de codificar aplicaciones para Android durante 2 años, esta pregunta golpeó bastante a la mayoría de la clase. Solo ahora vemos cuántas horas Java realmente nos ha ahorrado al no tener que rastrear errores desagradables de administración de memoria y no tener que escribir código de placa de caldera.
siamii
77
@NullUserException, ya que no especifica una forma de reclamar memoria que prácticamente implica un GC.
Winston Ewert
1
@ bizso09: ¿Ya viste ARC? No hay necesidad de GC lento / graso / no determinista cuando tiene soporte del sistema para el recuento de referencias: developer.apple.com/technologies/ios5
JBRWilkinson
3
Las respuestas a esta muy buena pregunta están llenas de mentiras religiosas.
abatishchev
1
En C y C ++ es posible tomar un puntero, convertirlo en int y agregarle un número. Luego reste el número del int y devuelva el resultado a un puntero. Obtendrá el mismo puntero que antes. Buena suerte al implementar un GC que NO recolecte esa memoria mientras su dirección se almacena solo en la variable que también tiene otro valor. Sé que el ejemplo es tonto, pero una lista vinculada de XOR usa algo similar. Publicaría esto como respuesta, pero la pregunta está cerrada.
Marian Spanik

Respuestas:

71

La recolección de basura requiere estructuras de datos para rastrear las asignaciones y / o el recuento de referencias. Estos crean gastos generales en la memoria, el rendimiento y la complejidad del lenguaje. C ++ está diseñado para estar "cerca del metal", en otras palabras, toma el lado de mayor rendimiento de las características de compensación frente a conveniencia. Otros idiomas hacen que esa compensación sea diferente. Esta es una de las consideraciones al elegir un idioma, qué énfasis prefiere.

Dicho esto, hay muchos esquemas para el recuento de referencias en C ++ que son bastante livianos y de alto rendimiento, pero están en bibliotecas, tanto comerciales como de código abierto, en lugar de ser parte del lenguaje en sí. El recuento de referencias para administrar la vida útil de los objetos no es lo mismo que la recolección de basura, pero aborda muchos de los mismos tipos de problemas y se ajusta mejor con el enfoque básico de C ++.

kylben
fuente
26
Un problema secundario es que el GC no es determinista. El objeto puede o no estar todavía en la memoria mucho después de que el programa lo haya "caído". Los ciclos de vida de recuento son deterministas, cuando se elimina la última referencia, se elimina la memoria. Esto tiene implicaciones no solo para la eficiencia de la memoria, sino también para la depuración. Un error de programación común es el objeto "zombie", memoria de referencia que teóricamente se ha descartado. Es mucho más probable que GC oculte este efecto y produzca errores intermitentes y extremadamente difíciles de rastrear.
kylben
22
- Los gc modernos no rastrean asignaciones ni cuentan referencias. Construyen un gráfico a partir de todo lo que está actualmente en la pila y simplemente condensan y borran todo lo demás (simplificado), y GC normalmente resulta en una complejidad de lenguaje reducida . Incluso el beneficio de rendimiento es cuestionable.
Joel Coehoorn
13
Er, @kylben, ¡el objetivo de tener GC automático en el lenguaje es que se hace imposible hacer referencia a objetos zombies, porque el GC solo libera objetos que son imposibles de hacer referencia! Obtiene el tipo de errores difíciles de rastrear de los que habla cuando comete errores con la administración manual de la memoria.
Ben
14
-1, GC no cuenta referencias. Además, dependiendo de su uso de memoria y esquema de asignación, un GC puede ser más rápido (con una sobrecarga en el uso de memoria). Entonces, el argumento sobre el rendimiento también es una falacia. Solo el cierre al metal es un punto válido en realidad.
deadalnix
14
Ni Java ni C # usan el recuento de referencias: los esquemas de GC basados ​​en el recuento de referencias son bastante primitivos en comparación y funcionan mucho peor que los recolectores de basura modernos (¡principalmente porque necesitan escribir en memoria para alterar los recuentos de referencias cada vez que copia una referencia!)
mikera
44

Estrictamente hablando, no hay administración de memoria en absoluto en el lenguaje C. malloc () y free () no son palabras clave en el lenguaje, sino funciones que se llaman desde una biblioteca. Esta distinción puede ser pedante ahora, porque malloc () y free () son parte de la biblioteca estándar de C, y será proporcionada por cualquier implementación de C que cumpla con los estándares, pero esto no siempre fue así en el pasado.

¿Por qué querrías un idioma sin estándar para la administración de memoria? Esto se remonta a los orígenes de C como 'ensamblaje portátil'. Existen muchos casos de hardware y algoritmos que pueden beneficiarse o incluso requerir técnicas especializadas de administración de memoria. Hasta donde sé, no hay forma de deshabilitar por completo la administración de memoria nativa de Java y reemplazarla por la suya. Esto simplemente no es aceptable en algunas situaciones de alto rendimiento / recursos mínimos. C proporciona una flexibilidad casi completa para elegir exactamente qué infraestructura utilizará su programa. El precio pagado es que el lenguaje C proporciona muy poca ayuda para escribir un código correcto y sin errores.

Charles E. Grant
fuente
2
+1 uno por la buena respuesta general, pero también especialmente por "El precio pagado es que el lenguaje C proporciona muy poca ayuda para escribir un código correcto y libre de errores"
Shivan Dragon
2
C tiene administración de memoria, pero simplemente funciona, por lo que la gente apenas lo nota. Hay memoria estática, registros y la pila. Hasta que comiences a asignar fuera del montón, estás bien y elegante. Es la asignación del montón lo que arruina las cosas. En cuanto a Java, todos pueden escribir su propio tiempo de ejecución de Java: hay mucho para elegir, incluido lo que podría llamarse "Java del sistema". .NET puede hacer casi todo lo que C puede: solo se queda atrás de las capacidades nativas de C ++ (por ejemplo, las clases se administran solo en .NET). Por supuesto, también tiene C ++. NET, que tiene todo lo que hace C ++ y todo lo que hace .NET.
Luaan
1
@Luaan Yo diría que es una definición muy generosa de tener "administración de memoria" "Hasta que comiences a asignar fuera del montón, estás bien y elegante. Es la asignación del montón lo que arruina las cosas", eso es como decir que un automóvil es un avión perfectamente bueno, simplemente no puede volar.
Charles E. Grant
1
@ CharlesE.Grant Bueno, un lenguaje puramente funcional puede hacer todo con ese tipo de gestión de memoria. El hecho de que la asignación de almacenamiento dinámico sea una buena compensación en algunos casos de uso no significa que sea el punto de referencia para todos los idiomas / tiempos de ejecución. No es como si la administración de memoria dejara de ser "administración de memoria" simplemente porque es simple, directo, oculto detrás de escena. El diseño de la asignación de memoria estática sigue siendo gestión de memoria, ya que es un buen uso de la pila y de cualquier otra cosa que tenga disponible.
Luaan
"cualquier implementación que cumpla con los estándares" no es cierta, solo para la implementación del entorno de host que cumple con los estándares. Algunas plataformas / bibliotecas estándar, la mayoría para microcontroladores integrados de 8 o 16 bits, no proporcionan malloc()o free(). (por ejemplo, compiladores MLAP para PIC)
12431234123412341234123
32

La verdadera respuesta es que la única forma de crear un mecanismo de recolección de basura seguro y eficiente es tener soporte a nivel de lenguaje para referencias opacas. (O, por el contrario, la falta de soporte a nivel de lenguaje para la manipulación directa de la memoria).

Java y C # pueden hacerlo porque tienen tipos de referencia especiales que no pueden ser manipulados. Esto le da al tiempo de ejecución la libertad de hacer cosas como mover objetos asignados en la memoria , que es crucial para una implementación de GC de alto rendimiento.

Para el registro, ninguna implementación moderna de GC utiliza el recuento de referencias , por lo que es completamente una pista falsa. Los GC modernos utilizan la colección generacional, donde las nuevas asignaciones se tratan esencialmente de la misma manera que las asignaciones de pila están en un lenguaje como C ++, y luego periódicamente los objetos recién asignados que aún están vivos se mueven a un espacio "sobreviviente" separado, y una generación entera de objetos se desasigna a la vez.

Este enfoque tiene ventajas y desventajas: la ventaja es que las asignaciones de montón en un lenguaje que admite GC son tan rápidas como las asignaciones de pila en un lenguaje que no admite GC, y la desventaja es que los objetos que necesitan realizar una limpieza antes de ser destruidos requieren un mecanismo separado (por ejemplo, la usingpalabra clave de C # ) o de lo contrario su código de limpieza se ejecuta de forma no determinista.

Tenga en cuenta que una clave para un GC de alto rendimiento es que debe haber soporte de idioma para una clase especial de referencias. C no tiene soporte para este lenguaje y nunca lo tendrá; debido a que C ++ tiene una sobrecarga del operador, podría emular un tipo de puntero GC'd, aunque tendría que hacerse con cuidado. De hecho, cuando Microsoft inventó su dialecto de C ++ que se ejecutaría bajo CLR (el tiempo de ejecución de .NET), tuvieron que inventar una nueva sintaxis para "referencias de estilo C #" (por ejemplo Foo^) para distinguirlas de "referencias de estilo C ++" (por ejemplo Foo&)

Lo que sí tiene C ++, y lo que usan regularmente los programadores de C ++, son punteros inteligentes , que en realidad son solo un mecanismo de conteo de referencias. No consideraría el conteo de referencias como GC "verdadero", pero proporciona muchos de los mismos beneficios, a costa de un rendimiento más lento que la gestión de memoria manual o el GC verdadero, pero con la ventaja de la destrucción determinista.

Al final del día, la respuesta realmente se reduce a una función de diseño del lenguaje. C hizo una elección, C ++ hizo una elección que le permitía ser compatible con versiones anteriores de C y al mismo tiempo proporcionar alternativas que son lo suficientemente buenas para la mayoría de los propósitos, y Java y C # hicieron una elección diferente que es incompatible con C pero que también es lo suficientemente buena para La mayoría de los propósitos. Desafortunadamente, no hay una bala de plata, pero estar familiarizado con las diferentes opciones disponibles le ayudará a elegir la correcta para cualquier programa que esté tratando de construir actualmente.

Daniel Pryden
fuente
44
Esta es la respuesta real a la pregunta
coredump
1
Para la parte c ++, hoy en día deberías mirar std :: unique_ptr y std :: move :)
Niclas Larsson
@NiclasLarsson: No estoy seguro de entender tu punto. ¿Estás diciendo que std::unique_ptres "soporte a nivel de lenguaje para referencias opacas"? (No era el tipo de soporte al que me refería, y tampoco creo que sea suficiente a menos que el soporte para la manipulación directa de la memoria también se elimine de C ++.) Menciono los punteros inteligentes en mi respuesta, y consideraría std:unique_ptrun puntero inteligente , dado que realmente hace un recuento de referencias, solo admite los casos especiales en los que el número de referencias es cero o uno (y std::movees el mecanismo de actualización del recuento de referencias).
Daniel Pryden
std::unique_ptrno tiene un recuento de referencias y std::moveno tiene nada que ver con las referencias (por lo tanto, "no" golpeó el rendimiento). Sin embargo, veo su punto, ya std::shared_ptrque tiene un recuento de referencias que es implícitamente actualizado por std::move:)
Niclas Larsson
2
@ Mike76: en el lado de la asignación, un asignador de GC funcionará casi tan rápido como la asignación de pila, y el GC puede distribuir miles de objetos al mismo tiempo. No importa lo que haga con una implementación de recuento de ref, la asignación y la desasignación nunca serán más rápidas que mallocy free. Entonces, sí, un GC puede ser sustancialmente más rápido. (Tenga en cuenta que dije "puede ser", por supuesto, el rendimiento exacto de cada programa se ve afectado por muchos factores).
Daniel Pryden
27

Porque, cuando se usa el poder de C ++, no hay necesidad.

Herb Sutter: " No he escrito eliminar en años " .

ver Escribir código C ++ moderno: cómo C ++ ha evolucionado a lo largo de los años 21:10

Puede sorprender a muchos programadores experimentados de C ++.

Lior Kogan
fuente
Interesante. Mi material de lectura para hoy.
surfasb
Bah, un video. Pero nunca menos, interesante ya.
surfasb
2
video interesante 21 minutos y 55 minutos fueron los mejores momentos. Lástima que las llamadas WinRT todavía parecieran ser C ++ / CLI bumpf.
gbjbaanb
2
@ dan04: Eso es cierto. Pero luego, si escribes en C, obtienes lo que pides.
DeadMG
66
Administrar los punteros inteligentes no es más exigente que asegurarse de no tener referencias innecesarias en un entorno de recolección de basura. Como GC no puede leer tu mente, tampoco es magia.
Tamás Szelei
15

"Todo" un recolector de basura es un proceso que se ejecuta periódicamente para verificar si hay objetos no referenciados en la memoria y si los elimina. (Sí, sé que esto es una simplificación excesiva). Esto no es una propiedad del lenguaje, sino del marco.

Hay recolectores de basura escritos para C y C ++, este por ejemplo.

Una razón por la que no se ha "agregado" al lenguaje podría deberse al gran volumen de código existente que nunca lo usaría, ya que usan su propio código para administrar la memoria. Otra razón podría ser que los tipos de aplicaciones escritas en C y C ++ no necesitan la sobrecarga asociada con un proceso de recolección de basura.

revs ChrisF
fuente
1
Pero los futuros programas escritos comenzarían a usar el recolector de basura, ¿no?
Dark Templar
55
Si bien la recolección de basura es teóricamente independiente de cualquier lenguaje de programación, es bastante difícil escribir un GC útil para C / C ++, e incluso imposible hacer uno infalible (al menos tan infalible como el de Java), la razón por la que Java puede extraerlo apagado es porque se ejecuta en un entorno virtualizado controlado. Por el contrario, el lenguaje Java está diseñado para GC, y tendrá dificultades para escribir un compilador Java que no haga GC.
tdammers
44
@tdammers: Estoy de acuerdo en que la recolección de basura debe ser compatible con el lenguaje para ser posible. Sin embargo, el punto principal no es la virtualización y el entorno controlado, sino una mecanografía estricta. C y C ++ tienen un tipo débil, por lo que permiten cosas como almacenar el puntero en una variable entera, reconstruir punteros a partir de desplazamientos y cosas que evitan que el recopilador pueda decir de manera confiable lo que se puede alcanzar (C ++ 11 prohíbe que la última permita al menos coleccionistas conservadores). En Java, siempre sabe qué es una referencia, por lo que puede recopilarlo con precisión, incluso si está compilado en nativo.
Jan Hudec
2
@ ThorbjørnRavnAndersen: puedo escribir un programa C válido que almacene punteros de tal manera que ningún recolector de basura pueda encontrarlos. Si luego conectas un recolector de basura a mallocy free, romperías mi programa correcto.
Ben Voigt
2
@ ThorbjørnRavnAndersen: No, no llamaría freehasta que hubiera terminado. Pero su recolector de basura propuesto que no libera la memoria hasta que llamo explícitamente freeno es un recolector de basura en absoluto.
Ben Voigt
12

C fue diseñado en una época en que la recolección de basura apenas era una opción. También estaba destinado a usos en los que la recolección de basura generalmente no funcionaría: entornos de metal desnudo en tiempo real con memoria mínima y soporte de tiempo de ejecución mínimo. Recuerde que C fue el lenguaje de implementación para el primer Unix, que se ejecutó en un pdp-11 con 64 * K * bytes de memoria. C ++ era originalmente una extensión de C: la elección ya se había hecho, y es muy difícil injertar la recolección de basura en un lenguaje existente. Es el tipo de cosa que debe construirse desde la planta baja.

ddyer
fuente
9

No tengo las citas exactas, pero tanto Bjarne como Herb Sutter dicen algo similar:

C ++ no necesita un recolector de basura, porque no tiene basura.

En C ++ moderno, utiliza punteros inteligentes y, por lo tanto, no tiene basura.

ronag
fuente
1
¿Qué son los punteros inteligentes?
Dark Templar
11
Si fuera así de simple, nadie habría implementado ningún GC.
deadalnix
77
@deadalnix: Correcto, porque nadie implementa nada demasiado complicado, lento, hinchado o innecesario. Todo el software es 100% eficiente todo el tiempo, ¿verdad?
Zach
55
@deadalnix: el enfoque de C ++ para la administración de memoria es más nuevo que los recolectores de basura. RAII fue inventado por Bjarne Stroustrup para C ++. La limpieza del destructor es una idea anterior, pero las reglas para garantizar la seguridad de la excepción son clave. No sé exactamente cuándo se describió por primera vez la idea en sí, pero el primer estándar de C ++ se finalizó en 1998, y el "Diseño y evolución de C ++" de Stroustrups no se publicó hasta 1994, y las excepciones fueron una adición relativamente reciente a C ++: después de la publicación del "Manual de referencia anotado de C ++" en 1990, creo. GC fue inventado en 1959 para Lisp.
Steve314
1
@deadalnix: ¿sabe que al menos una máquina virtual Java utiliza un GC de recuento de referencias que podría (casi) implementarse usando RAII estilo C ++ usando una clase de puntero inteligente, precisamente porque era más eficiente para el código multiproceso que las máquinas virtuales existentes? Consulte www.research.ibm.com/people/d/dfb/papers/Bacon01Concurrent.pdf. Una razón por la que no ve esto en C ++ en la práctica es la colección GC habitual: puede recopilar ciclos, pero no puede elegir un orden de destructor seguro en presencia de ciclos y, por lo tanto, no puede garantizar una limpieza confiable del destructor.
Steve314
8

Pregunta por qué estos idiomas no se han actualizado para incluir un recolector de basura opcional.

El problema con la recolección de basura opcional es que no puede mezclar código que usa los diferentes modelos. Es decir, si escribo un código que asume que está utilizando un recolector de basura, no puede usarlo en su programa que tiene desactivada la recolección de basura. Si lo hace, se filtrará por todas partes.

Winston Ewert
fuente
6

¿Te imaginas escribir un controlador de dispositivo en un idioma con recolección de basura? ¿Cuántos bits podrían aparecer en la línea mientras el GC estaba funcionando?

O un sistema operativo? ¿Cómo podría comenzar a ejecutar la recolección de basura antes incluso de iniciar el núcleo?

C está diseñado para bajo nivel cerca de las tareas de hardware. ¿El problema? es un lenguaje tan agradable que también es una buena opción para muchas tareas de nivel superior. Los zares del lenguaje son conscientes de estos usos, pero deben admitir los requisitos de los controladores de dispositivos, el código incrustado y los sistemas operativos como prioridad.

James Anderson
fuente
2
C bueno para alto nivel? Resoplé mi bebida por todo el teclado.
DeadMG
55
Bueno, él dijo "muchas tareas de nivel superior". Él podría ser trol de recuento (uno, dos, muchos ...). Y en realidad no dijo más alto que qué. Sin embargo, aparte de las bromas, es cierto: la evidencia es que muchos proyectos significativos de alto nivel se han desarrollado con éxito en C. Puede haber mejores opciones ahora para muchos de esos proyectos, pero un proyecto en funcionamiento es una evidencia más sólida que la especulación sobre lo que podría Ha estado.
Steve314
Hay algunos sistemas operativos administrados, y funcionan bastante bien. De hecho, cuando hace que todo el sistema sea administrado, el rendimiento del uso del código administrado disminuye aún más, hasta ser más rápido que el código no administrado en escenarios de la vida real. Por supuesto, esos son todos "SO de investigación": prácticamente no hay forma de hacerlos compatibles con el código no administrado existente además de crear un SO no virtualizado completamente virtualizado dentro del SO administrado. Sin embargo, Microsoft sugirió en algún momento que podrían reemplazar Windows Server con uno de esos, ya que cada vez se escribe más código de servidor en .NET.
Luaan
6

La respuesta breve y aburrida a esta pregunta es que es necesario que exista un lenguaje que no recolecte basura para las personas que escriben los recolectores de basura. Conceptualmente no es fácil tener un lenguaje que al mismo tiempo permita un control muy preciso sobre el diseño de la memoria y tenga un GC ejecutándose en la parte superior.

La otra pregunta es por qué C y C ++ no tienen recolectores de basura. Bueno, sé que C ++ tiene un par de ellos, pero no son muy populares porque se ven obligados a lidiar con un lenguaje que no fue diseñado para ser GC-ed en primer lugar, y las personas que todavía usan C ++ en esta edad no es realmente del tipo que pierde un GC.

Además, en lugar de agregar GC a un antiguo lenguaje no editado por GC, en realidad es más fácil crear un nuevo lenguaje que tenga la mayor parte de la misma sintaxis mientras admite un GC. Java y C # son buenos ejemplos de esto.

hugomg
fuente
1
En algún lugar en programmers.se o SO, hay una afirmación que alguien me hizo de que alguien estaba trabajando en una cosa recolectada de basura con autoinicio: IIRC básicamente implementa la VM usando un lenguaje GC, con un subconjunto de inicio utilizado para implementar el GC en sí. Se me olvida el nombre. Cuando lo examiné, resultó que básicamente nunca habían logrado el salto del subconjunto sin GC al nivel de GC de trabajo. Esto es posible en principio, pero AFAIK nunca se ha logrado en la práctica, sin duda se trata de hacer las cosas de la manera difícil.
Steve314
@ Steve314: ¡Me encantaría ver eso si alguna vez recuerdas dónde lo encontraste!
hugomg
¡Lo encontré! Vea los comentarios a stackoverflow.com/questions/3317329/… que hacen referencia a la máquina virtual Klein. Parte del problema para encontrarlo: la pregunta estaba cerrada.
Steve314
Por cierto, parece que no puedo comenzar mis comentarios con @missingno, ¿qué pasa?
Steve314
@ steve314: Una vez que recibí la respuesta a la que se adjunta este hilo, ya recibí una notificación para todos los comentarios. Hacer una @ -post en este caso sería redundante y SE no lo permite ( aunque no me pregunte por qué ). (Sin embargo, la verdadera causa es porque falta mi número)
hugomg
5

Hay varios problemas, incluyendo ...

  • Aunque GC se inventó antes de C ++, y posiblemente antes de C, tanto C como C ++ se implementaron antes de que los GC fueran ampliamente aceptados como prácticos.
  • No puede implementar fácilmente un lenguaje y plataforma GC sin un lenguaje subyacente que no sea GC.
  • Aunque el GC es demostrablemente más eficiente que el no GC para el código de aplicaciones típico desarrollado en escalas de tiempo típicas, etc., existen problemas en los que un mayor esfuerzo de desarrollo es una buena compensación y la gestión de memoria especializada superará a un GC de uso general. Además, C ++ generalmente es demostrablemente más eficiente que la mayoría de los lenguajes GC, incluso sin ningún esfuerzo de desarrollo adicional.
  • GC no es universalmente más seguro que RAII estilo C ++. RAII permite que se limpien automáticamente otros recursos además de la memoria, básicamente porque admite destructores confiables y oportunos. Estos no se pueden combinar con los métodos convencionales de GC debido a problemas con los ciclos de referencia.
  • Los lenguajes GC tienen sus propios tipos característicos de pérdidas de memoria, particularmente en relación con la memoria que nunca se volverá a utilizar, pero donde existieron referencias existentes que nunca se han anulado o sobrescrito. La necesidad de hacer esto explícitamente no es diferente en principio a la necesidad deleteo freeexplícitamente. El enfoque de GC todavía tiene una ventaja, sin referencias colgantes, y el análisis estático puede detectar algunos casos, pero nuevamente, no hay una solución perfecta para todos los casos.

Básicamente, en parte se trata de la edad de los idiomas, pero de todos modos siempre habrá un lugar para los idiomas que no son GC, incluso si es un lugar un tanto nicho. Y en serio, en C ++, la falta de GC no es un gran problema: su memoria se administra de manera diferente, pero no está sin administrar.

Microsofts administrado C ++ tiene al menos cierta capacidad de mezclar GC y no GC en la misma aplicación, lo que permite una combinación de las ventajas de cada uno, pero no tengo la experiencia para decir qué tan bien funciona en la práctica.

Enlaces de representación de putas a respuestas mías relacionadas ...

Steve314
fuente
4

La recolección de basura es fundamentalmente incompatible con un lenguaje de sistemas utilizado para desarrollar controladores para hardware compatible con DMA.

Es completamente posible que el único puntero a un objeto se almacene en un registro de hardware en algún periférico. Dado que el recolector de basura no sabría sobre esto, pensaría que el objeto era inalcanzable y lo recogería.

Este argumento es doble para la compactación de GC. Incluso si tuviera cuidado de mantener referencias en memoria a los objetos utilizados por los periféricos de hardware, cuando el GC reubicara el objeto, no sabría cómo actualizar el puntero contenido en el registro de configuración de periféricos.

Entonces, ahora necesitaría una mezcla de memorias intermedias DMA inmóviles y objetos administrados por GC, lo que significa que tiene todas las desventajas de ambos.

Ben Voigt
fuente
Posiblemente todas las desventajas de ambos, pero menos casos de cada desventaja, y lo mismo para las ventajas. Claramente, es complejo tener que lidiar con más tipos de gestión de memoria, pero también puede evitarse la complejidad al elegir el caballo adecuado para cada curso dentro de su código. Es poco probable, me imagino, pero hay una brecha teórica allí. He especulado sobre mezclar GC y no GC en el mismo idioma antes, pero no para los controladores de dispositivos, más por tener una aplicación GC en su mayoría, pero con algunas bibliotecas de estructura de datos de bajo nivel administradas manualmente por la memoria.
Steve314
@ Steve314: ¿No diría que recordar qué objetos deben liberarse manualmente es tan oneroso como recordar liberar todo? (Por supuesto, los punteros inteligentes pueden ayudar con cualquiera de ellos, por lo que ninguno de los dos es un gran problema) Y necesita diferentes grupos para objetos administrados manualmente versus objetos recolectados / compactables, ya que la compactación no funciona bien cuando hay objetos fijos dispersos por todas partes. Así que mucha complejidad extra para nada.
Ben Voigt
2
No si hay una clara división entre el código de alto nivel que es todo GC y el código de bajo nivel que se excluye de GC. Desarrollé la idea principalmente mientras miraba D hace algunos años, lo que le permite optar por no participar en GC pero no le permite volver a ingresar. Tome, por ejemplo, una biblioteca de árbol B +. El contenedor en su conjunto debería ser GC, pero los nodos de la estructura de datos probablemente no; es más eficiente hacer un escaneo personalizado a través de los nodos hoja solo que hacer que el GC haga una búsqueda recursiva a través de los nodos ramificados. Sin embargo, esa exploración necesita informar los elementos contenidos al GC.
Steve314
El punto es que es una pieza contenida de funcionalidad. Tratar los nodos del árbol B + como gestión de memoria WRT especial no es diferente a tratarlos como WRT especiales que son nodos del árbol B +. Es una biblioteca encapsulada, y el código de la aplicación no necesita saber que el comportamiento del GC ha sido omitido / en mayúsculas especiales. Excepto que, al menos en ese momento, eso era imposible en D; como dije, no hay forma de volver a participar e informar los elementos contenidos al GC como posibles raíces del GC.
Steve314
3

Porque, C y C ++ son lenguajes de nivel relativamente bajo destinados a fines generales, incluso, por ejemplo, para ejecutarse en un procesador de 16 bits con 1 MB de memoria en un sistema integrado, que no podía permitirse el desperdicio de memoria con gc.

Petruza
fuente
1
"Sistema Integrado"? Cuando se estandarizó C (1989), necesitaba poder manejar PC con 1 MB de memoria.
dan04
Estoy de acuerdo, estaba citando un ejemplo más actual.
Petruza
1MB ??? Santo Schmoley, ¿quién necesitaría tanta RAM? </billGates>
Mark K Cowan
2

Hay recolectores de basura en C ++ y C. No estoy seguro de cómo funciona esto en C, pero en C ++ puede aprovechar RTTI para descubrir dinámicamente su gráfico de objetos y usarlo para la recolección de basura.

Que yo sepa, no puede escribir Java sin un recolector de basura. Una pequeña búsqueda resultó esto .

La diferencia clave entre Java y C / C ++ es que en C / C ++ la elección siempre es suya, mientras que en Java a menudo no tiene opciones por diseño.

back2dos
fuente
Y también que los recolectores de basura dedicados están mejor implementados, son más eficientes y se adaptan mejor al lenguaje. :)
Max
No, no puede usar RTTI para descubrir dinámicamente el gráfico de objetos en C / C ++: son los objetos de datos antiguos que estropean todo. Simplemente no hay información RTTI almacenada en un objeto de datos antiguo que permita a un recolector de basura diferenciar entre punteros y no punteros dentro de ese objeto. Peor aún, los punteros no necesitan estar perfectamente alineados en todo el hardware, por lo tanto, dado un objeto de 16 bytes, hay 9 ubicaciones posibles en las que se puede almacenar un puntero de 64 bits, solo dos de los cuales no se superponen.
cmaster
2

Es una compensación entre rendimiento y seguridad.

No hay garantía de que su basura se recolecte en Java, por lo que puede quedarse usando espacio durante mucho tiempo, mientras que el escaneo de objetos sin referencia (es decir, basura) también lleva más tiempo que eliminar o liberar explícitamente un objeto no utilizado.

La ventaja es, por supuesto, que uno puede construir un lenguaje sin punteros o sin pérdidas de memoria, por lo que es más probable que produzca el código correcto.

A veces puede haber una ligera ventaja "religiosa" en estos debates: ¡ten cuidado!

adrianmcmenamin
fuente
1

Aquí hay una lista de problemas inherentes de GC, que lo hacen inutilizable en un lenguaje de sistema como C:

  • El GC tiene que ejecutarse por debajo del nivel del código cuyos objetos gestiona. Simplemente no existe tal nivel en un núcleo.

  • Un GC tiene que detener el código administrado de vez en cuando. Ahora piense en lo que sucedería si le hiciera eso a su núcleo. Todo el procesamiento en su máquina se detendría, por ejemplo, un milisegundo, mientras que el GC escanea todas las asignaciones de memoria existentes. Esto acabaría con todos los intentos de crear sistemas que funcionen bajo estrictos requisitos en tiempo real.

  • Un GC debe ser capaz de distinguir entre punteros y no punteros. Es decir, debe ser capaz de mirar cada objeto de memoria existente y ser capaz de producir una lista de desplazamientos donde se puedan encontrar sus punteros.

    Este descubrimiento debe ser perfecto: el GC debe ser capaz de perseguir todos los punteros que descubre. Si desreferenciara un falso positivo, probablemente se estrellaría. Si no logra descubrir un falso negativo, probablemente destruiría un objeto que todavía está en uso, bloqueando el código administrado o corrompiendo silenciosamente sus datos.

    Esto requiere absolutamente que la información de tipo se almacene en cada objeto existente. Sin embargo, tanto C como C ++ permiten objetos de datos antiguos simples que no contienen información de tipo.

  • GC es un negocio inherentemente lento. Los programadores que se han socializado con Java pueden no darse cuenta de esto, pero los programas pueden ser mucho más rápidos cuando no se implementan en Java. Y uno de los factores que hace que Java sea lento es GC. Esto es lo que impide que los lenguajes GCed como Java se utilicen en la supercomputación. Si su máquina cuesta un millón al año en consumo de energía, no desea pagar ni siquiera el 10% de eso por la recolección de basura.

C y C ++ son lenguajes que se crean para admitir todos los casos de uso posibles. Y, como puede ver, muchos de estos casos de uso están excluidos de la recolección de basura. Entonces, para admitir estos casos de uso, C / C ++ no se puede recolectar basura.

cmaster
fuente