En informática , la basura incluye datos , objetos u otras regiones de la memoria de un sistema informático (u otros recursos del sistema) que no se utilizarán en ningún cálculo futuro por parte del sistema o de un programa que se ejecute en él. Debido a que cada sistema informático tiene una cantidad finita de memoria y la mayoría del software produce basura, con frecuencia es necesario desasignar la memoria que está ocupada por basura y devolverla al montón o grupo de memoria para su reutilización.
La basura se clasifica generalmente en dos tipos: basura sintáctica , cualquier objeto o dato que se encuentra dentro del espacio de memoria de un programa pero que no es accesible desde el conjunto raíz del programa ; y basura semántica , cualquier objeto o dato al que nunca accede un programa en ejecución para cualquier combinación de entradas de programa. Los objetos y datos que no son basura se denominan activos .
En términos simples, la basura sintáctica son datos a los que no se puede acceder, y la basura semántica son datos a los que no se accederá. Más precisamente, la basura sintáctica son datos a los que no se puede acceder debido al gráfico de referencia (no hay una ruta hacia él), lo que se puede determinar mediante muchos algoritmos, como se explicó en el seguimiento de la recolección de basura , y solo requiere analizar los datos, no el código. La basura semántica son datos a los que no se accederá, ya sea porque son inalcanzables (por lo tanto, también basura sintáctica), o son accesibles pero no se accederá; esto último requiere el análisis del código y, en general, es un problema indecidible .
La basura sintáctica es un subconjunto (normalmente estricto) de la basura semántica, ya que es totalmente posible que un objeto contenga una referencia a otro objeto sin utilizar nunca ese objeto.
En la siguiente implementación de pila simple en Java, cada elemento extraído de la pila se convierte en basura semántica una vez que no hay referencias externas a él: [a]
clase pública Pila { privada Objeto [] elementos ; privado int tamaño ; public Stack ( int capacidad ) { elementos = new Object [ capacidad ] ; } public void push ( Objeto e ) { elementos [ tamaño ++] = e ; } public Object pop () { return elementos [-- tamaño ] ; } }
Esto se debe a que elements[]
todavía contiene una referencia al objeto, pero nunca se volverá a acceder al objeto a través de esta referencia, porque elements[]
es privado para la clase y el pop
método solo devuelve referencias a elementos que aún no ha extraído. (Después de que decrementa size
, esta clase nunca volverá a acceder a ese elemento). Sin embargo, saber esto requiere un análisis del código de la clase, que es indecidible en general.
Si una push
llamada posterior vuelve a hacer crecer la pila al tamaño anterior, sobrescribiendo esta última referencia, entonces el objeto se convertirá en basura sintáctica, porque nunca se podrá acceder nuevamente a él y será elegible para la recolección de basura.
Un ejemplo de recolección automática de basura sintáctica, mediante la recolección de basura por conteo de referencias , se puede producir utilizando el intérprete de línea de comandos de Python :
>>> class Foo : ... """Esta es una clase de prueba vacía.""" ... pass ... >>> bar = Foo () >>> bar <__main__.Foo object at 0x54f30> >>> del bar
En esta sesión se crea un objeto, se muestra su ubicación en la memoria y luego se destruye la única referencia al objeto; a partir de este momento, no hay forma de volver a utilizar el objeto, ya que no hay referencias a él. Esto se hace evidente cuando intentamos acceder a la referencia original:
>>> bar Traceback (última llamada más reciente): Archivo "<stdin>" , línea 1 , en ? NameError : el nombre 'bar' no está definido
Como ahora es imposible hacer referencia al objeto, este se ha vuelto inútil; es basura. Dado que Python utiliza la recolección de basura, automáticamente desasigna la memoria que se utilizó para el objeto para que pueda volver a utilizarse:
>>> clase Bar : ... """Esta es otra clase de prueba.""" ... pasar ... >>> baz = Bar () >>> baz <__main__.Bar object at 0x54f30>
La instancia Bar ahora reside en la ubicación de memoria 0x54f30 ; en el mismo lugar donde se encontraba nuestro objeto anterior, la instancia Foo . Dado que la instancia Foo fue destruida, liberando la memoria utilizada para contenerla, el intérprete crea el objeto Bar en la misma ubicación de memoria que antes, haciendo un buen uso de los recursos disponibles.
La basura consume memoria del montón y, por lo tanto, se desea recolectarla (para minimizar el uso de memoria, permitir una asignación de memoria más rápida y evitar errores de falta de memoria al reducir la fragmentación del montón y el uso de memoria).
Sin embargo, la recolección de basura lleva tiempo y, si se hace manualmente, requiere una sobrecarga de codificación. Además, la recolección de basura destruye objetos y, por lo tanto, puede provocar llamadas a finalizadores , ejecutando código potencialmente arbitrario en un punto arbitrario en la ejecución del programa. La recolección de basura incorrecta (desasignación de memoria que no es basura), principalmente debido a errores en la recolección de basura manual (en lugar de errores en los recolectores de basura), da como resultado violaciones de seguridad de memoria (que a menudo crean agujeros de seguridad) debido al uso de punteros colgantes .
La basura sintáctica se puede recolectar automáticamente y los recolectores de basura se han estudiado y desarrollado ampliamente. La basura semántica no se puede recolectar automáticamente en general y, por lo tanto, causa fugas de memoria incluso en lenguajes que recolectan basura. La detección y eliminación de basura semántica generalmente se realiza utilizando una herramienta de depuración especializada llamada generador de perfiles de montón, que permite ver qué objetos están activos y cómo se puede acceder a ellos, lo que permite eliminar la referencia no deseada.
El problema de la gestión de la desasignación de basura es bien conocido en informática. Se adoptan varios enfoques: