La recogida de basura es una característica clave del lenguaje de programación Java que gestiona automáticamente la asignación y liberación de memoria de los objetos que se crean en un espacio eden.
La recogida de basura en Java permite a los desarrolladores centrarse en escribir código sin preocuparse por la gestión de la memoria, lo que convierte a Java en una opción popular para crear aplicaciones complejas y a gran escala. Sin embargo, comprender cómo funciona la recogida de basura es esencial para que los desarrolladores de Java optimicen el rendimiento de su código y eviten errores comunes relacionados con la memoria.
En esta guía se exploran los fundamentos de la recogida de basura en Java, incluidas sus ventajas, los distintos tipos de recolectores y las mejores prácticas a seguir durante la codificación. Así que, ¡vamos a sumergirnos y explorar cómo funciona la recogida de basura!
Boletín del sector
Manténgase al día sobre las tendencias más importantes e intrigantes del sector en materia de IA, automatización, datos y mucho más con el boletín Think. Consulte la Declaración de privacidad de IBM.
Su suscripción se enviará en inglés. Encontrará un enlace para darse de baja en cada boletín. Puede gestionar sus suscripciones o darse de baja aquí. Consulte nuestra Declaración de privacidad de IBM para obtener más información.
OutofMemoryError es un tipo de error que ocurre cuando un programa o aplicación intenta asignar más memoria de la cantidad disponible. Este error se produce cuando la máquina virtual Java (JVM) u otra plataforma se queda sin memoria al intentar ejecutar una aplicación.
Un OutofMemoryError se suele producir cuando una aplicación o un programa intenta crear nuevos objetos, pero la JVM no puede asignar la memoria para alojarlos. Este error también puede ocurrir cuando una aplicación está usando demasiada memoria y no la está liberando correctamente.
Cuando se produce un OutofMemoryError, la aplicación normalmente se bloquea y se cierra. Este error es común en los programas que gestionan grandes cantidades de metadatos, como las aplicaciones de procesamiento de imágenes o vídeos, o en los programas que gestionan bases de datos grandes.
Para resolver este error, es posible que tenga que aumentar la cantidad de memoria disponible para la aplicación u optimizar el uso de memoria de la aplicación. Esto se puede hacer modificando los parámetros de la JVM o utilizando una herramienta de creación de perfiles de memoria para identificar las pérdidas de memoria o el uso ineficiente de la memoria.
En Java, todos los objetos se almacenan en el montón, que es una parte de la memoria que está reservada para la asignación dinámica de objetos. Cuando ya no se hace referencia a un objeto en ninguna parte del programa, pasa a ser susceptible de recogida de basura.
El recolector de basura en Java escanea periódicamente la memoria del montón para encontrar objetos que no se están utilizando. El proceso de recogida de basura implica varios pasos, incluidos el marcado, el barrido y la compactación.
Marcado: el primer paso de la recogida de basuras consiste en marcar todos los objetos a los que el programa sigue haciendo referencia. Esto se hace comenzando con un conjunto de objetos raíz, como variables globales, variables locales y parámetros de métodos, y luego rastreando todos los objetos que son alcanzables desde esas raíces. Los objetos que no pueden alcanzarse desde las raíces se consideran aptos para la recogida de basura.
Barrido: después de la fase de marcado, el recolector de elementos no utilizados barre el montón de Java para identificar y recuperar la memoria que utilizan los objetos a los que ya no se hace referencia. Esto implica retirar la memoria utilizada por los objetos no utilizados y añadirla de nuevo a la reserva de memoria libre.
Compactación: en algunos algoritmos de recogida de basura, la fase de barrido va seguida de una fase de compactación, en la que la memoria que utilizan los objetos restantes se reorganiza para minimizar la fragmentación. Esto implica acercar los objetos y crear bloques contiguos más grandes de memoria libre.
La máquina virtual Java (JVM) realiza automáticamente la recogida de basura, por lo que el programador no tiene que gestionar manualmente la memoria. El recolector de basura se ejecuta en un hilo independiente y suele funcionar en segundo plano, por lo que no afecta a la ejecución normal del programa.
Hay dos tipos principales de algoritmos de recogida de basura en Java: recogida de basura completa y recogida de basura incremental.
La recogida de basura completa es un proceso en el que un recolector de basuras (una parte del sistema en tiempo de ejecución de un lenguaje de programación) busca en toda la memoria que utiliza un programa y compila los objetos que ya no utiliza el programa. Estos objetos se marcan entonces como basura y son susceptibles de ser eliminados de la memoria.
La recogida de basura completa suele realizarla mediante el sistema de ejecución de un lenguaje de programación que utiliza la administración automática de memoria, como Java o Python. Durante el proceso, el recolector de basura detiene la ejecución del programa para realizar la búsqueda de objetos basura, lo que puede provocar una ralentización temporal del rendimiento del programa.
La recogida de basura completa se suele activar cuando la cantidad de memoria utilizada por un programa alcanza un umbral determinado o cuando el programa solicita un nuevo bloque de memoria y no hay suficiente memoria libre disponible. El objetivo de la recogida de basura completa es recuperar la memoria que el programa no necesita y ponerla a disposición para que la utilicen otras partes del programa o otros programas que se ejecuten en la misma máquina.
La recogida de basura incremental es un tipo de técnica de gestión de memoria que utilizan los lenguajes de programación y los entornos de tiempo de ejecución para recuperar automáticamente la memoria que un programa ya no necesita. Lo hace identificando objetos en la memoria que no se están utilizando y liberando la memoria que ocupan para que otras partes del programa puedan reutilizarla.
En la recogida de basura incremental, el recolector de basura escanea periódicamente la memoria del programa en busca de objetos inalcanzables en la memoria del montón de la generación joven. En lugar de detener la ejecución del programa durante este proceso de escaneo, el recolector de basura divide el proceso de escaneo en partes pequeñas y manejables llamadas "incrementos". Durante cada incremento, el recolector de elementos no utilizados escanea una parte de la memoria del programa, identificando los objetos que no son necesarios y marcándolos como disponibles para su reutilización.
Mediante el uso de incrementos, el recolector de basura puede recuperar memoria en pequeños fragmentos, sin interrumpir la ejecución del programa durante un período prolongado. Esto ayuda a garantizar que el programa siga respondiendo y no experimente pausas o retrasos significativos como resultado del proceso de recogida de basura.
Sin embargo, la recogida de basura incremental puede ser menos eficiente que otros tipos de técnicas de recogida de basura, como la recogida de basura generacional o de marcado y barrido, porque requiere escaneos más frecuentes de la memoria del programa. Además, el uso de incrementos puede introducir cierta sobrecarga en la ejecución del programa, ya que el recolector de basura necesita mantener la información de estado entre cada incremento.
En general, la recogida de basura de Java proporciona muchos beneficios que la convierten en una herramienta valiosa para los desarrolladores. Estos son algunos de los beneficios de usar la recogida de basura de Java:
No hay gestión manual de la memoria: con la recogida de basura, los desarrolladores no tienen que gestionar manualmente la asignación y desasignación de memoria. Esto significa que los programadores pueden centrarse más en escribir código y menos en gestionar la memoria, lo que puede ayudar a reducir los errores y mejorar la productividad.
Evita las fugas de memoria: la recogida de basura ayuda a prevenir las fugas de memoria, que pueden producirse cuando un programa no libera la memoria que ya no se necesita. Esto puede provocar que el programa consuma más memoria de la necesaria, lo que ralentiza el rendimiento y, finalmente, se bloquea.
La memoria se asigna de manera dinámica: la recogida de basura de Java permite la asignación dinámica de memoria, lo que significa que la memoria se asigna según sea necesario en tiempo de ejecución. Esto ayuda a evitar errores de asignación de memoria y puede hacer que el programa sea más eficiente.
Mejor rendimiento: la recogida de basura puede ayudar a mejorar el rendimiento de un programa al reducir la cantidad de tiempo que se dedica a gestionar la memoria. Esto puede conducir a tiempos de ejecución más rápidos y a un programa más receptivo.
Optimiza la memoria: la recogida de basura puede optimizar el uso de la memoria reutilizando la memoria que no utiliza una parte del programa para otras partes del programa. Esto puede ayudar a reducir el uso de memoria y mejorar la eficiencia general del programa.
Los desarrolladores se benefician de la capacidad de recogida de basura de Java para administrar automáticamente la memoria, evitar fugas de memoria, habilitar la asignación dinámica de memoria, mejorar el rendimiento y optimizar el uso de la memoria, la recolección de basura puede ayudar a los desarrolladores a escribir programas mejores y más eficientes.
En Java, la JVM (Máquina virtual Java) activa automáticamente la recogida de basura cuando determina que el montón se está llenando o cuando ha pasado una cierta cantidad de tiempo.
Hay varios eventos que pueden desencadenar la recogida de basura en Java:
Asignación de espacio de almacenamiento dinámico: cuando la JVM necesita asignar memoria para un nuevo objeto y no hay suficiente espacio en el almacenamiento dinámico, activa la recogida de basura para recuperar la memoria no utilizada o almacenarla en el espacio superviviente.
Uso del método System.gc(): puede solicitar explícitamente la recogida de basura mediante el método System.gc() aunque no hay garantía de que se ejecute.
Umbral de generación anterior: la recogida de basura también se puede activar cuando el tamaño de almacenamiento dinámico del espacio de almacenamiento dinámico de generación anterior (que almacena objetos de larga duración) alcanza un umbral determinado.
Umbral de PermGen/MetaSpace: en las versiones de Java anteriores a Java 8, la recogida de basura también se puede activar cuando el tamaño de las áreas de memoria de PermGen (generación permanente) o Metaspace (en Java 8 y versiones posteriores) alcanza un umbral determinado.
Basado en el tiempo: a veces, la recogida de basura se puede activar en función de un intervalo de tiempo. Por ejemplo, la JVM podría activar la recogida de basura cada hora o todos los días, independientemente del uso de la memoria.
Vale la pena señalar que el comportamiento exacto de la recogida de basura en Java puede variar según la implementación y configuración de JVM.
Para solicitar que la máquina virtual Java (JVM) ejecute el recolector de basura, puede seguir estos pasos:
Usar del método System.gc(): este método se utiliza para solicitar a la JVM que ejecute el recolector de basura. No se garantiza que el recolector de basura se ejecute inmediatamente después de emplear este método.
Utilizar el indicador -XX:+DisableExplicitGC JVM: este indicador desactiva las solicitudes explícitas de recogida de basura. Esto significa que incluso si usa System.gc() o Runtime.getRuntime().gc(), el recolector de basura no se activará.
Es importante tener en cuenta que, por lo general, no se recomienda solicitar explícitamente la ejecución del recolector de basura, ya que la JVM está diseñada para gestionar automáticamente la asignación de memoria y la recolección de basura. Las solicitudes explícitas de recolección de basura a veces pueden tener un impacto negativo en el rendimiento.
En un lenguaje de programación, un objeto es susceptible de ser recogido cuando ya no se hace referencia a él en ninguna parte del programa. La recogida de basura automática es un proceso realizado por el entorno de tiempo de ejecución del lenguaje de programación para recuperar memoria.
En la mayoría de los lenguajes de programación modernos, el entorno de tiempo de ejecución realiza automáticamente la recogida de basura. Los algoritmos específicos que se utilizan para la recogida de basura pueden variar en función del lenguaje de programación y la implementación, pero el principio general es el mismo: el entorno de tiempo de ejecución explora periódicamente el montón (la parte de la memoria que se utiliza para los objetos asignados dinámicamente) para identificar los objetos que ya no son accesibles desde cualquier objeto vivo en el programa. Una vez que se identifica un objeto como no accesible, se marca como basura y se puede recuperar su memoria.
El momento exacto en el que un objeto es elegible para la recogida de basura depende del algoritmo de recogida de basura específico que utilice el entorno de tiempo de ejecución. Algunos algoritmos son más agresivos que otros y pueden recuperar memoria más rápidamente, mientras que otros pueden retrasar la recogida de basura para optimizar el rendimiento. Sin embargo, en general, el programador no necesita preocuparse por administrar la memoria manualmente, ya que el entorno de tiempo de ejecución se encarga de esto automáticamente.
Hay varios recolectores de basura de Java, entre ellos:
Recolector de basura en serie: el recolector en serie es el recolector de basura predeterminado en Java y se utiliza normalmente en aplicaciones pequeñas y medianas que no requieren un alto rendimiento. Este tipo de recolector ayuda a evitar que se produzcan los eventos comunes de "parar el mundo".
Recolector de basura paralelo: el recolector paralelo está diseñado para aplicaciones de alto rendimiento y es particularmente útil en aplicaciones que requieren grandes montones porque utiliza varias CPU para acelerar el proceso. Es importante tener en cuenta que este tipo de recolector congela los subprocesos de la aplicación cuando se ejecuta un recolector de basura.
Recolector de barrido de marcas concurrentes (CMS): el recolector CMS está diseñado para aplicaciones que requieren tiempos de pausa bajos y es útil en aplicaciones que tienen muchos objetos vivos.
Recolector de basura G1: el recolector G1 está diseñado para grandes montones y puede manejar una combinación de objetos de corta y larga duración. Utiliza varios subprocesos para escanear y compactar simultáneamente el montón.
Cada recolector de basura tiene sus propios puntos fuertes y débiles, y la elección de qué recolector utilizar depende de las necesidades específicas de la aplicación. También es posible configurar y ajustar la configuración del recolector de basura para optimizar el rendimiento de una aplicación en particular.
La recogida de basura y las pérdidas de memoria están relacionadas con la gestión de la memoria en los programas informáticos, pero tienen significados e implicaciones diferentes.
Como se ha dicho anteriormente, la recogida de basura la realiza normalmente el lenguaje de programación o el entorno de tiempo de ejecución, y ayuda a garantizar que los programas no consumen más memoria de la que necesitan. La recogida de basura identifica la memoria que está libre para ser utilizada por otras partes del programa o por otros programas que se ejecuten en el ordenador.
Por otro lado, una pérdida de memoria ocurre cuando un programa no libera la memoria que ha asignado, incluso cuando esa memoria ya no es necesaria. Como resultado, el programa sigue consumiendo memoria con el tiempo, lo que lleva al agotamiento final de la memoria disponible, que puede causar que el programa o todo el sistema operativo se bloquee. Las pérdidas de memoria suelen ser causadas por errores en el programa y pueden ser difíciles de identificar y solucionar.
En resumen, la recogida de basura es un proceso para liberar automáticamente la memoria que ya no se necesita. Las fugas de memoria se producen cuando un programa asigna memoria pero no la libera, lo que provoca una acumulación gradual del uso de la memoria.
En conclusión, la recogida de basura es un aspecto esencial de la programación Java que garantiza una gestión eficiente de la memoria al recuperar la memoria no utilizada. La observabilidad de Instana proporciona a los desarrolladores herramientas poderosas para monitorizar y optimizar el proceso de recogida de basura en tiempo real.
Al usar Instana, los desarrolladores pueden identificar rápidamente las pérdidas de memoria, optimizar la configuración de recogida de basura y solucionar los problemas de rendimiento relacionados con la recogida de basura.
Gracias a las completas capacidades de monitorización de Instana, los desarrolladores pueden obtener información detallada sobre el uso de la memoria y el comportamiento de la recolección de elementos no utilizados de sus aplicaciones Java, lo que les permite ofrecer software fiable y de alto rendimiento.
Si se siguen las buenas prácticas descritas en esta guía, los desarrolladores pueden utilizar Instana para optimizar el proceso de recogida de basura y mejorar el rendimiento general de sus aplicaciones Java. Con la observabilidad de Instana, los desarrolladores pueden anticiparse a cualquier problema que pueda surgir, asegurándose de que sus aplicaciones funcionen siempre al máximo.
Un servicio totalmente gestionado y de inquilino único para desarrollar y entregar aplicaciones Java.
Utilice el software y las herramientas de DevOps para crear, implementar y gestionar aplicaciones nativas de la nube en varios dispositivos y entornos.
El desarrollo de aplicaciones en la nube significa crear una vez, iterar rápidamente e implementar en cualquier lugar.