universidades - tesis de residuos solidos municipales
¿Hubo una razón específica por la cual la recolección de basura no fue diseñada para C? (13)
He oído que era subóptimo que C recolectara basura automáticamente. ¿Hay algo de verdad en esto?
¿Hubo una razón específica por la cual no se implementó la recolección de basura para C?
1972.
C fue diseñado en 1972.
Peor aún, c fue diseñado en hardware obsoleto. En 1972.
No me malinterpretes Tenían recolección de basura en 1972, pero todos los problemas de los que se quejan las personas hasta el día de hoy eran preocupaciones reales y muy válidas en ese momento.
C es un lenguaje de muy bajo nivel. Es el tipo de lenguaje que puede elegir para escribir un lenguaje de nivel superior con elementos como la recolección de basura. Es pequeño y simple, y hace exactamente lo que pide.
C ++ se basó en C + y agregó una administración de memoria más sofisticada / automática (como llamar a los destructores cuando los objetos quedaron fuera del alcance). Entonces puede preguntarse por qué C ++ no tiene recolección de basura, en cuyo caso vea lo que Stroustrup tiene que decir : brevemente, la gente quiere hacer las cosas de una manera más directa, y la gente que realmente la quiere puede usar una biblioteca (que también puede ser utilizado en C).
C es un lenguaje muy antiguo y carece de muchas de las campanas y silbatos de los idiomas modernos. Para agregar la recolección de basura ahora se requeriría una importante respuesta del idioma. En general, cualquiera que esté dispuesto a realizar tantos cambios en C preferiría crear su propio idioma.
Por lo general, agregar la recolección automática de basura a un idioma reducirá el rendimiento o hará que la recolección de basura se realice en momentos no previstos. Agregar la recolección de basura a C causaría que pierda una de sus ventajas comparativas, ya que puede usarse para la programación a nivel de sistema que requiere tiempos de respuesta en tiempo real o casi en tiempo real.
C se inventó a principios de la década de 1970 para escribir sistemas operativos y otras cosas de bajo nivel. Los recolectores de basura estaban presentes (por ejemplo, las primeras versiones de Smalltalk) pero dudo que estuvieran preparados para funcionar en un entorno tan liviano, y habría todas las complicaciones de trabajar con buffers y punteros de muy bajo nivel.
Como lenguaje, C fue diseñado intencionalmente para ser realmente pequeño. Tiene muy pocas operaciones o funciones intrínsecas, y la mayoría de ellas reflejan las instrucciones básicas que se encuentran en las CPU. A menudo se le llama "lenguaje ensamblador portátil".
Eso lo hace ideal para escribir programas portátiles para cambiar las llamadas telefónicas, que se ejecutan en pequeñas computadoras con muy poca memoria, que es parte de lo que Bell Labs buscaba a principios de los 70 cuando empezaron a trabajar en C y Unix.
Eso también hace que C sea ideal para escribir kernels de sistemas operativos, controladores de dispositivos, etc. Dichos ejecutables no pueden contar con tener entornos de ejecución sofisticados y ricos como la recolección de basura, o incluso solo la asignación de memoria dinámica como malloc (). Esto se debe a que en realidad forman la base de esos entornos, por lo que te encuentras con el problema de "levantarte por los bootstraps".
La gente ha escrito bibliotecas de asignación de memoria con recolección de basura para C. Pero las bibliotecas no son parte del lenguaje C en sí misma, y es muy improbable que algo tan complejo sea aceptado como parte de las bibliotecas en C estándar. Los programadores de C son muy conservadores en cuanto a los cambios en el estándar.
La fortaleza (y la debilidad) de C es que es pequeña, simple, liviana y adecuada para aplicaciones de sistemas y aplicaciones integradas. GC agregaría gastos generales, y ya no sería pequeño y simple. GC para C simplemente no es apropiado . No es más complicado que eso.
La recolección de basura se ha implementado para C (por ejemplo, el colector Boehm-Demers-Weiser ). No se especificó que C incluyera GC cuando era nuevo por varias razones, en gran parte porque para el hardware que estaban dirigiendo y el sistema que estaban construyendo, simplemente no tenía mucho sentido.
Editar (para responder a algunas alegaciones planteadas elsethread):
Para que GC conservador esté bien definido, básicamente solo tiene que hacer un cambio en el lenguaje: diga que cualquier cosa que haga que un puntero sea temporalmente "invisible" lleva a un comportamiento indefinido. Por ejemplo, en la C actual puede escribir un puntero a un archivo, sobrescribir el puntero en la memoria, luego leerlo nuevamente y (asumiendo que era previamente válido) todavía acceder a los datos a los que apunta. Un GC no necesariamente se "daría cuenta" de que el puntero existía, por lo que podría ver que la memoria ya no es accesible, y por lo tanto está abierta a la colección, por lo que la desreferencia posterior no "funcionará".
En cuanto a la recolección de basura no es determinista: hay recolectores en tiempo real que son absolutamente deterministas y se pueden usar en sistemas en tiempo real difíciles. También hay administradores de pilas deterministas para la administración manual, pero la mayoría de los administradores manuales no son deterministas.
En cuanto a la recolección de basura que es lenta y / o la memoria caché: técnicamente, esto es cierto, pero es puramente un tecnicismo. Si bien los diseños (p. Ej., El aprovechamiento generacional) que (al menos en su mayoría) evitan estos problemas son bien conocidos, está abierto el argumento de que no son exactamente una recolección de basura (aunque hacen casi lo mismo para el programador).
En cuanto al GC que se ejecuta en momentos desconocidos o inesperados: esto no es necesariamente más o menos cierto que con la memoria administrada manualmente. Puede hacer que un GC se ejecute en un subproceso separado que se ejecute (al menos un poco) de forma impredecible. Lo mismo ocurre con los bloques libres coalescentes con la administración de memoria manual. Un intento particular de asignar memoria puede desencadenar un ciclo de recopilación, lo que hace que algunas asignaciones sean mucho más lentas que otras; lo mismo ocurre con un administrador manual que usa la fusión perezosa de bloques libres.
Curiosamente, GC es mucho menos compatible con C ++ que con C. La mayoría de C ++ depende de que los destructores sean invocados de manera determinista, pero con la recolección de basura ya no es así. Esto rompe un montón de código, y cuanto mejor escrito esté el código, mayor será el problema que generalmente causa.
Del mismo modo, C ++ requiere que
std::less<T>
proporcione resultados significativos (y, lo que es más importante, coherentes) para los punteros, incluso cuando apuntan a objetos completamente independientes. Requeriría un poco de trabajo extra para cumplir con este requisito con un recolector / recolector de copias (pero estoy bastante seguro de que es posible). Aún es más difícil lidiar con (por ejemplo) alguien que revisa una dirección y espera resultados consistentes. Esta es generalmente una mala idea, pero sigue siendo posible y debería producir resultados consistentes.
La recolección de basura no es útil si desea realizar optimizaciones extremas, porque si decide el momento en el que es mejor liberar espacio asignado, puede hacer un trabajo mucho mejor en su proyecto que el recolector de basura. En la década de 1970, no sé la causa exacta por la que los desarrolladores decidieron no incluir un recolector de basura en C, probablemente querían tener un control absoluto de la desasignación, pero estoy seguro de que más tarde ni siquiera consideraron agregar una basura recolector, porque incluso si hay una integración de un recolector de basura al lenguaje que es muy bueno, se construyeron muchos proyectos en C y las nuevas versiones del lenguaje deben tener compatibilidad con versiones anteriores.
EDITAR: "También hay compiladores, bibliotecas y mecanismos a nivel del sistema operativo para realizar la verificación de los límites de la matriz, la detección de desbordamiento del búfer, la serialización y la recolección automática de basura, que no son una parte estándar de C." Esto puede ser interesante para usted, puede leer más here .
La recolección de basura podría implementarse para C, aunque con el hardware de CPU normal sería algo difícil e ineficiente. Para que la recolección de basura funcione realmente bien, se necesitaría una arquitectura que utilizara el direccionamiento de base + desplazamiento para todo; la parte base de cada puntero debería apuntar siempre al comienzo de una región asignada. Tenga en cuenta que si la base fuera un "selector" en lugar de una dirección lineal, un selector de 32 bits más un desplazamiento de 32 bits podría acceder a 4 mil millones de objetos, cada uno de los cuales podría tener hasta 4 gigas. Creo que un diseño de este tipo podría ser más eficiente que el direccionamiento lineal de 64 bits para muchas aplicaciones, ya que las referencias y las compensaciones de objetos utilizarían la mitad de espacio de caché que con los punteros de 64 bits. Usar 64 bits simplemente parece un desperdicio.
Por cierto, una arquitectura que utiliza una dirección base + de desplazamiento para todo tendría otra ventaja sobre un sistema de direccionamiento lineal: la recolección de basura podría ejecutarse simultáneamente con otras operaciones. La única vez que la recolección de basura tendría que molestar a un subproceso en ejecución sería si el subproceso intentara acceder a un objeto de datos mientras se reubicaba ese objeto en particular. Bajo ese escenario, el intento de reubicación tendría que abortarse (suponiendo que fuera una copia no superpuesta), o el subproceso tendría que esperar a que se copiara ese objeto en particular. Una situación mucho mejor para el código en tiempo real de la que existe cuando todos los subprocesos deben suspenderse para GC.
Lo siento, pero decir que C no tiene GC porque es antiguo o porque fue diseñado para un hardware diferente es un poco ridículo.
Preguntar por qué C no tiene GC, pero en otros idiomas es como preguntar "¿por qué un martillo tiene un extremo romo mientras que un hacha tiene un extremo afilado"? Bueno, los idiomas son herramientas, y las diferentes herramientas fueron diseñadas para construir diferentes programas con diferentes requisitos. Algunos programas, como los controladores de dispositivos o los kernels del sistema operativo, tienen requisitos que hacen que sea casi imposible implementar la recolección de basura mientras se satisfacen, por lo que se necesitaba un lenguaje como C, por lo que se creó C.
Al igual que cualquier otra herramienta, es así porque los problemas que debía resolver obligaron a ser así. Las personas que dicen que fue porque se creó en los años 70 o debido a un hardware antiguo hacen que parezca completamente circunstancial. Si esto fuera cierto, C se habría ido con los años 70, pero sigue siendo un lenguaje popular.
Además, gc es muy difícil de implementar limpiamente sin algún tipo de sistema de tiempo de ejecución, pero C está destinado a ejecutarse en hardware.
Me gusta lo que JWZ tiene que decir sobre C. :-)
C es "el ensamblador PDP-11 que piensa que es un lenguaje".
Por lo tanto, si observa cómo crearía un ensamblador portátil, C parece totalmente correcto sin recolección de basura, por la simple razón de que las CPU no tienen una instrucción de " recolección de basura ". *
(Aunque la mayoría de las demás instrucciones de la CPU se capturan bien con C hasta el día de hoy, algunas no lo son, como la falta de expresividad de Cs para las operaciones SIMD, etc.)
* Sí, sé que algunos de ustedes encontrarán un ejemplo para demostrar que estoy equivocado. Pero en el caso general ...
No escuche que "C es viejo y por eso no tiene GC". Hay problemas fundamentales con GC que no se pueden superar, lo que los hace incompatibles con C.
El mayor problema es que la recolección de basura precisa requiere la capacidad de escanear la memoria e identificar cualquier puntero encontrado. Algunos lenguajes de nivel superior limitan los enteros para no usar todos los bits disponibles, de modo que los bits altos se pueden usar para distinguir las referencias de los objetos de los enteros. Dichos lenguajes pueden entonces almacenar cadenas (que podrían contener secuencias de octetos arbitrarias) en una zona de cadena especial donde no se pueden confundir con punteros, y todo está bien. Sin embargo, la implementación de AC no puede hacer esto porque los bytes, los enteros más grandes, los punteros y todo lo demás se pueden almacenar juntos en estructuras, uniones o como parte de fragmentos devueltos por malloc
.
¿Qué sucede si desecha el requisito de precisión y decide que está de acuerdo con algunos objetos que nunca se liberan porque algunos datos que no son punteros en el programa tienen el mismo patrón de bits que las direcciones de estos objetos? Ahora suponga que su programa recibe datos del mundo exterior (red / archivos / etc.). Afirmo que puedo hacer que su programa pierda una cantidad arbitraria de memoria y, finalmente, se quede sin memoria, siempre que pueda adivinar suficientes punteros y emularlos en las cadenas que alimento su programa. Esto se vuelve mucho más fácil si aplica las secuencias De Bruijn .
Aparte de eso, la recolección de basura es simplemente lenta. Puedes encontrar cientos de académicos a quienes les gusta afirmar lo contrario, pero eso no cambiará la realidad. Los problemas de rendimiento de GC se pueden dividir en 3 categorías principales:
- Imprevisibilidad
- Contaminación de caché
- Tiempo dedicado a caminar todo el recuerdo.
Las personas que reclamarán GC es rápido en estos días, simplemente lo están comparando con lo que no es correcto: programas C y C ++ mal escritos que asignan y liberan miles o millones de objetos por segundo. Sí, estos también serán lentos, pero al menos predeciblemente lentos de una manera que puede medir y corregir si es necesario. Un programa de C bien escrito pasará tan poco tiempo en malloc
/ free
que los gastos generales ni siquiera serán medibles.
Objective_C es ANSI C con algo así como 12 palabras clave agregadas y un tipo llamado id. Tiene recogida de basuras. Utiliza el conteo de referencias.