La respuesta real es que la única forma de crear un mecanismo de recolección de basura seguro y eficiente es tener soporte a nivel de idioma para referencias opacas. (O, a la inversa, una falta de soporte a nivel de idioma para la manipulación directa de la memoria).
Java y C # pueden hacerlo porque tienen tipos de referencia especiales que no se pueden manipular. Esto le da al tiempo de ejecución la libertad de hacer cosas como mover objetos asignados en la memoria , lo cual 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 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 separado de "sobreviviente", y toda una generación de los objetos se desasigna a la vez.
Este enfoque tiene ventajas y desventajas: la ventaja es que las asignaciones de pila en un lenguaje que admite GC son tan rápidas como las asignaciones de pila en una lengua que no es compatible con GC, y la desventaja es que los objetos que necesitan realizar una limpieza antes de ser destruidos requieren un mecanismo separado (por ejemplo, la palabra clave using
de C #) o su código de limpieza se ejecuta de manera no determinista.
Tenga en cuenta que una de las claves de un GC de alto rendimiento es que debe haber soporte de idioma para una clase especial de referencias. C no tiene este soporte de idioma y nunca lo hará; Como C ++ tiene una sobrecarga de operadores, 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 el CLR (el tiempo de ejecución .NET), tuvieron que inventar una nueva sintaxis para "referencias de estilo C #" (por ejemplo, Foo^
) para distinguirlas de "C ++ - referencias de estilo "(por ejemplo, Foo&
).
Lo que C ++ tiene y lo que usan regularmente los programadores de C ++ es punteros inteligentes , que en realidad son solo un mecanismo de conteo de referencias. No consideraría que el recuento de referencias sea un GC "verdadero", pero proporciona muchos de los mismos beneficios, a costa de un rendimiento más lento que la administración de memoria manual o un 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 de idioma. C hizo una elección, C ++ hizo una elección que le permitía ser compatible con versiones anteriores de C mientras seguía ofreciendo 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 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 que sea más adecuada para el programa que esté intentando construir.