La memoria en Java

Como he tenido que empaparme de su funcionamiento, aprovecho y pongo un post sobre conceptos básicos de la memoria en Java.

Para empezar, Java divide la memoria en dos segmentos bien diferenciados:

  • Heap: Objetos del usuario, variables, …
  • Non-Heap/Perm: Código, metadatos, …

La que más nos interesa es la Heap, porque es la que podemos “controlar”. A la memoria Non-Heap (Perm) se le puede configurar el tamaño con el comando MaxPermSize. Pero esto sólo es útil si la aplicación va a cargar o generar dinámicamente muchas clases diferentes.

La memoria heap se divide a su vez en dos generaciones según su tiempo de vida:

  • Young Generation
  • Old Generation

Normalmente la generación joven está compuesta de variables locales y objetos temporales, mientras que la generación vieja suele componerse de estructuras que son necesarias durante toda la ejecución: configuraciones, ventanas gráficas,…
As I had to study their operation, I decided to post about the basic concepts of memory in Java.

To begin wtih, Java divides memory into two distinct segments:

  • Heap: User objects, variables, …
  • Non-Heap/Perm: code, metadata, …

The one that interests us is the Heap, as it is what we can “control”. You can configure the Non-Heap Memory (Perm) size with the parameter MaxPermSize. But this is only useful if the application is loaded or dynamically generate many different classes.

The heap is divided into two generations according to their lifetime:

  • Young Generation
  • Old Generation

Usually the younger generation is composed of local variables and temporary objects, while the older generation usually consists of structures that are necessary during the execution: configurations, viewports, …

A su vez, la generación joven se divide en dos:

  • Eden: Aquí es donde se crean los objetos inicialmente
  • Survivor: Es como el limbo a través del cual se pasa de la generación joven a la vieja. Suele estar compuesto a su vez en dos partes.

Se puede especificar el ratio entre el espacio de la generación joven y el espacio de la generación vieja con el comando de la máquina virtual -XX:NewRatio.

El Recolector de Basura (Garbage Collector) realiza dos tipos de tareas periódicas:

  • Minor Collection: Que revisa de forma rápida la generación joven.
  • Major Collection: Que revisa de forma exhaustiva toda la memoria, fundamentalmente la generación vieja.

El Recolector de Basura se ejecuta a la vez que la ejecución normal del programa, por lo que cada ejecución suya implica una pequeña pausa (de milisegundos normalmente) en todos los threads que se estén ejecutando en ese momento. Mientras la memoria permanezca más o menos liberada, el Recolector de Basura se limitará a las minor collections, para no interferir con el flujo de la aplicación.

Para tener una idea más exacta de cómo trabaja, se puede añadir la opción -verbose:gc a la máquina virtual. Este comando irá imprimiendo cada vez que se ejecute el Recolector de Basura cuanto espacio de memoria ha conseguido liberar.

Existen varias implementaciones del Recolector de Basura, siendo la más común el Serial Collector. Esta implementación, además de ser la más sencilla, utiliza sólo un procesador. Si se está usando una máquina más potente, con varios procesadores y una buena cantidad de memoria física, se puede activar el Parallel Collector, que utiliza varias CPUs a la vez. Esto mejora la forma en la que funciona el Recolector de Basura, pero también puede paralizar a ratos el flujo de ejecución normal de la aplicación.

De todas formas, para el buen funcionamiento y limpieza de la memoria, conviene tener pequeños objetos temporales de vida corta antes que objetos largos y duraderos. Los pequeños objetos temporales se quedarán en el Eden, por lo que se recolectarán mucho antes y de forma mucho más rápida.

Así mismo, tener objetos en memoria sin usar, aunque no molesten a la ejecución del programa, ralentizan la ejecución del Recolector de Basura, porque tendrá que procesarlos una y otra vez en todas sus pasadas.

En algún momento puede parecer tentador forzar una ejecución del Recolector de Basura llamando a System.gc(). Sin embargo, esto lo único que hará será forzar una major collection de forma asíncrona, rompiendo completamente toda la heurística del Recolector de Basura. Es tan desaconsejable que hasta hay una opción en la máquina virtual para desactivar estas llamadas: -XX:+DisableExplicitGC

Para ayudar a la tarea del Recolector de Basura, existen tres tipos de referencias:

  • Weak: No impide al recolector de basura llevárselo.
  • Soft: Como el weak, pero el recolector de basura lo respeta un poco más y sólo se lo lleva si le hace falta memoria. Útil para cachés, pero puede ser engañoso.
  • Phantom: Siempre devuelve null, no accede al objeto realmente, pero puede servir servir para limpiar antes de que se lleven al objeto que lo enlaza.

Una clase que explica bastante bien el funcionamiento de este tipo referencias es WeakHashMap: Funciona como un HashMap, pero usando referencias Weak. De esta forma, si las claves que contiene sólo están referenciadas en dicho HashMap, considera que ya no son útiles y las borra.

Referencias útiles:


Also, the younger generation is divided into two:

  • Eden: This is where objects are created initially
  • Survivor: It’s like the limbo through which we pass from the young to the old generation. It is usually composed into two parts.

You can specify the ratio between the space of the young generation and old generation with the virtual machine parameter -XX: NewRatio .

The Garbage Collector ( Garbage Collector ) performs two types of periodic tasks:

  • Minor Collection: Reviews quickly the younger generation.
  • Major Collection: Reviews all the memory, mainly the older generation.

The garbage collector runs at the same time as the normal program execution and each execution involves a small pause (usually milliseconds) in all the threads that are running at that time. As memory remains more or less freed, the Garbage Collector will be limited to minor collections, to not interfere with the flow of the application.

To get a better idea of ​​how it works, you can add -verbose:gc to the virtual machine. This command will print each time you run the garbage collector and the area of ​​memory that has been successfully unleashed.

There are several implementations of the garbage collector, being the most common the Serial Collector. This implementation, as well as being the simplest, uses only one processor. If you’re using a more powerful machine with multiple processors and a good amount of physical memory, you can activate the Parallel Collector , which uses multiple CPUs at the same time. This improves the way in which the garbage collector works, but it can also paralyze the flow of normal execution of the application.

For proper operation and cleaning of memory, we should have little short-lived temporary objects better than long, durable objects. The small temporary objects will stay in the Eden, so they will be collected much earlier and much faster.

Also, having unused objects in memory, although they do not disrupt the execution of the program, will slow the execution of the garbage collection, because you have to process them over and over again to check if they can be deleted.

At some point it may seem tempting to force a Garbage Collector implementation calling System.gc() . However, this will force a major collection asynchronously, breaking up all the heuristics of the Garbage Collector. It is so discouraged that there is an option in the virtual machine to disable these calls: -XX: + DisableExplicitGC .

To help the task of garbage collection, there are three types of references :

  • Weak : It does not prevent the garbage collector take it.
  • Soft : As the weak, but the garbage collector a little more respect him and takes him only if you need memory. Useful for caching, but can be misleading.
  • Phantom: Always returns null, not really accesses the object, but can serve serve to clear before they take the object that binds it.

A class that explains quite well the operation of such references is WeakHashMap : it works as a HashMap, but using Weak References. Thus, if the key contains an object which is only referenced in the HashMap, it is no longer considered useful and is cleared.

Useful references:

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *