Stack Memory y Heap Space en Java

1. Introducción

Para ejecutar una aplicación de manera óptima, JVM divide la memoria en pila y memoria de pila. Siempre que declaramos nuevas variables y objetos, llamamos a un nuevo método, declaramos un String o realizamos operaciones similares, JVM designa memoria para estas operaciones desde Stack Memory o Heap Space.

En este tutorial, discutiremos estos modelos de memoria. Enumeraremos algunas diferencias clave entre ellos, cómo se almacenan en la RAM, las funciones que ofrecen y dónde usarlas.

2. Apilar memoria en Java

Stack Memory en Java se utiliza para la asignación de memoria estática y la ejecución de un hilo. Contiene valores primitivos que son específicos de un método y referencias a objetos que están en un montón, referidos desde el método.

El acceso a esta memoria se realiza en el orden Último en entrar, primero en salir (LIFO). Siempre que se llama a un nuevo método, se crea un nuevo bloque en la parte superior de la pila que contiene valores específicos de ese método, como variables primitivas y referencias a objetos.

Cuando el método finaliza la ejecución, su marco de pila correspondiente se vacía, el flujo vuelve al método de llamada y queda espacio disponible para el siguiente método.

2.1. Características clave de la memoria de pila

Además de lo que hemos discutido hasta ahora, a continuación se presentan algunas otras características de la memoria de pila:

  • Crece y se reduce a medida que se llaman y devuelven nuevos métodos, respectivamente.
  • Las variables dentro de la pila existen solo mientras se esté ejecutando el método que las creó
  • Se asigna y desasigna automáticamente cuando el método finaliza la ejecución
  • Si esta memoria está llena, Java arroja java.lang.StackOverFlowError
  • El acceso a esta memoria es rápido en comparación con la memoria del montón
  • Esta memoria es segura para subprocesos ya que cada subproceso opera en su propia pila

3. Heap Space en Java

El espacio de pila en Java se usa para la asignación de memoria dinámica para objetos Java y clases JRE en el tiempo de ejecución . Los objetos nuevos siempre se crean en el espacio dinámico y las referencias a estos objetos se almacenan en la memoria de la pila.

Estos objetos tienen acceso global y se puede acceder a ellos desde cualquier lugar de la aplicación.

Este modelo de memoria se divide en partes más pequeñas llamadas generaciones, que son:

  1. Generación joven: aquí es donde se asignan y envejecen todos los objetos nuevos. Se produce una recolección de basura menor cuando se llena
  2. Generación antigua o titular: aquí es donde se almacenan los objetos que han sobrevivido durante mucho tiempo. Cuando los objetos se almacenan en la generación joven, se establece un umbral para la edad del objeto y cuando se alcanza ese umbral, el objeto se traslada a la generación anterior.
  3. Generación permanente: consiste en metadatos de JVM para las clases de tiempo de ejecución y los métodos de aplicación.

Estas diferentes partes también se tratan en este artículo: Diferencia entre JVM, JRE y JDK.

Siempre podemos manipular el tamaño de la memoria del montón según nuestro requisito. Para obtener más información, visite este artículo vinculado de Baeldung.

3.1. Características clave de Java Heap Memory

Además de lo que hemos discutido hasta ahora, a continuación se presentan algunas otras características del espacio de almacenamiento dinámico:

  • Se accede a él a través de técnicas complejas de gestión de memoria que incluyen generación joven, generación antigua o titular y generación permanente.
  • Si el espacio del montón está lleno, Java arroja java.lang.OutOfMemoryError
  • El acceso a esta memoria es relativamente más lento que la memoria de pila
  • Esta memoria, a diferencia de la pila, no se desasigna automáticamente. Necesita Garbage Collector para liberar objetos no utilizados para mantener la eficiencia del uso de la memoria
  • A diferencia de la pila, un montón no es seguro para subprocesos y debe protegerse sincronizando correctamente el código

4. Ejemplo

Según lo que hemos aprendido hasta ahora, analicemos un código Java simple y evaluemos cómo se administra la memoria aquí:

class Person { int id; String name; public Person(int id, String name) { this.id = id; this.name = name; } } public class PersonBuilder { private static Person buildPerson(int id, String name) { return new Person(id, name); } public static void main(String[] args) { int id = 23; String name = "John"; Person person = null; person = buildPerson(id, name); } }

Analicemos esto paso a paso:

  1. Al ingresar al método main () , se crearía un espacio en la memoria de la pila para almacenar primitivas y referencias de este método
    • El valor primitivo de la identificación entera se almacenará directamente en la memoria de la pila
    • La variable de referencia person de tipo Person también se creará en la memoria de pila que apuntará al objeto real en el montón
  2. La llamada al constructor parametrizado Person (int, String) desde main () asignará más memoria sobre la pila anterior. Esto almacenará:
    • La referencia de este objeto del objeto de llamada en la memoria de pila
    • La identificación del valor primitivo en la memoria de pila
    • La variable de referencia del nombre del argumento de cadena que apuntará a la cadena real del grupo de cadenas en la memoria del montón
  3. El método principal es además llamar al método estático buildPerson () , para lo cual se llevará a cabo una asignación adicional en la memoria de pila además de la anterior. Esto almacenará de nuevo las variables de la manera descrita anteriormente.
  4. Sin embargo, para el objeto person de tipo Person recién creado , todas las variables de instancia se almacenarán en la memoria del montón.

Esta asignación se explica en este diagrama:

5. Resumen

Antes de concluir este artículo, resumamos rápidamente las diferencias entre Stack Memory y Heap Space:

Parámetro Memoria de pila Espacio de almacenamiento dinámico
Solicitud La pila se usa en partes, una a la vez durante la ejecución de un hilo Toda la aplicación usa espacio de pila durante el tiempo de ejecución
Talla La pila tiene límites de tamaño según el sistema operativo y generalmente es más pequeña que Heap No hay límite de tamaño en Heap
Almacenamiento Almacena solo variables primitivas y referencias a objetos que se crean en Heap Space Todos los objetos recién creados se almacenan aquí
Orden Se accede mediante el sistema de asignación de memoria Last-in First-out (LIFO) Se accede a esta memoria a través de técnicas complejas de gestión de memoria que incluyen la generación joven, la generación anterior o titular y la generación permanente.
Vida La memoria de pila solo existe mientras se esté ejecutando el método actual El espacio de pila existe siempre que se ejecute la aplicación
Eficiencia Comparativamente mucho más rápido de asignar en comparación con el montón Más lento de asignar en comparación con la pila
Asignación / Desasignación Esta memoria se asigna y desasigna automáticamente cuando se llama y se devuelve un método, respectivamente El espacio de pila se asigna cuando Gargabe Collector crea y desasigna nuevos objetos cuando ya no se hace referencia a ellos.

6. Conclusión

Pila y montón son dos formas en las que Java asigna memoria. En este artículo, entendimos cómo funcionan y cuándo usarlos para desarrollar mejores programas Java.

Para obtener más información sobre la administración de memoria en Java, consulte este artículo aquí. También discutimos el recolector de basura JVM que se analiza brevemente en este artículo.