Cómo se usa la RAM

¡Ayuda! ¡Linux se come mi RAM!

Foto de Liam Briese vía Unsplash

En varias ocasiones me han usado como argumento que las últimas versiones de los sistemas operativos casi no dejan RAM libre. Las variantes son múltiples: «No me queda memoria libre», «la nueva versión de Windows es peor, me consume toda la memoria», «Linux se come mi RAM» o cualquier otra afirmación equivalente. Vamos a analizar muy someramente parte de la gestión de memoria para explicar qué es lo que realmente sucede y porqué esto no es así.

Este artículo es una traducción o adaptación libre del trabajo de Vidar Holen, tanto del sitio «Linux ate my RAM» como de su página de experimentos con memoria RAM.

Tux comiendo un DIMM de RAM
Tux comiendo un DIMM de RAM, la emblemática cabecera usada en «Linux ate my RAM»

Los artículos originales no indican explícitamente los términos bajo los que se licencian. Aunque porciones del contenido de ambas páginas se han copiado en múltiples sitios, en muchos casos sin citar la fuente, se ha considerado adecuado comprobar si se podía realizar la traducción o si era necesario pedir permiso para ello.

Tras indagar en el repositorio de Linux ate my RAM!, hay un par de preguntas relativas a la traducción y licencias en las que el autor autoriza la traducción al francés y conoce de una traducción al ruso e indica su preferencia por la licencia CC0.

Por todo ello, se considera que www.linuxatemyram.com está bajo licencia CC0 y es lícito realizar esta traducción o adaptación.

Ronda de preguntas

¿Qué sucede?

Linux está prestando la memoria no asignada a la cache de disco. Cuando se tenga que acceder a esa información en un futuro se accederá desde la memoria RAM, que es mucho más rápida que el disco.

Por esto parece que el equipo tiene poca memoria, pero no es así. Todo está bien.

¿Por qué hace eso?

La cache de disco hace que el sistema sea mucho más rápido y responda más fluido. No tiene ninguna contraindicación o inconveniente, a excepción de la confusión que genera. En ningún caso reduce la memoria que pueden utilizar las aplicaciones.

¿Qué pasa si quiero ejecutar más aplicaciones?

Si las aplicaciones necesitan memoria, se devuelve parte de la RAM que se había prestado a la cache de disco. Las aplicaciones que lo necesiten pueden utilizar toda la memoria ocupada por la cache de disco de forma inmediata.

¿Necesito incrementar la swap de mi sistema?

No, la cache de disco sólo toma prestada la memoria que no está siendo usada por aplicaciones. En ningún caso utiliza swap1.

Según las aplicaciones necesiten memoria y mientras sea posible, se irá reduciendo la cache de disco antes de hacer swapping.

¿Cómo puedo evitar que Linux haga esto?

La respuesta corta: No se puede deshabilitar la cache de disco.

La única razón por la que alguien quiera deshabilitar la cache de disco es porque piense que está reduciendo la memoria disponible para las aplicaciones y esto no es así. La caché de disco hace que las aplicaciones carguen más rápido y ejecuten de forma más fluida, pero nunca jamás (valga la redundancia) reduce la memoria disponible para las aplicaciones. Por eso no existe ninguna razón para deshabilitarla.

Sin embargo, si se necesita limpiar rápidamente la RAM para solucionar algún otro tipo de problema, como un mal comportamiento, se puede forzar el vaciado de las cachés ejecutando echo 3 | sudo tee /proc/sys/vm/drop_caches o cualquier variante equivalente. No obstante, tras forzar que se vacíe la cache, ésta se volverá a regenerar automáticamente según se acceda al disco.

¿Por qué top y free indican que toda mi RAM está utilizada si no es así?

Es una simple confusión de términos.

No hay duda de que la memoria asignada a aplicaciones está usada mientras que la memoria que no se está utilizando para nada está libre. Pero ¿cómo se denomina la memoria que está utilizada para algo y, al mismo tiempo, libre para asignarla a una aplicación que la necesite? Se podría responder tanto libre como usada.

La memoria que estáAplicaciónLinux
🔴 usada por las aplicaciones🔴 Usada🔴 Usada
🟡 usada y disponible (caches)🟢 Libre🔴 Usada
🟢 sin utilizar🟢 Libre🟢 Libre

Desde el punto de vista de la aplicación o del usuario, la memoria está libre ya que se puede usar inmediatamente. Sin embargo, dado que realmente no está libre, Linux la contabiliza como usada y disponible al mismo tiempo.

Ese asignada en algo, pero disponible para utilizar si hace falta es lo que top y free denominan available, anteriormente buffers/cached. Esta discrepancia de terminología genera la confusión.

Entonces, ¿Cómo puedo ver cuánta memoria RAM libre tengo realmente?

Para saber cuánta memoria RAM pueden utilizar las aplicaciones sin utilizar swap, se puede ejecutar free (con la opción -m para que muestre los valores en MiB) y fijarse en la columna available.

                total        used        free      shared  buff/cache   available
  Mem:           1504        1491          13           0         855      792
  Swap:          2047           6        2041

Mirar únicamente las columnas used o free puede llevar a la conclusión que la RAM está al 99 %, cuando realmente está al 47 %.

Los dos formatos de salida de free

En versiones previas, la salida de free era diferente y bastante más confusa. En estas versiones la salida correcta está en la columna free de la fila -/+ buffers/cache.

             total       used       free     shared    buffers     cached
Mem:          1504       1491         13          0         91        764
-/+ buffers/cache:        635        869
Swap:         2047          6       2041

Tradicionalmente se sumaba la memoria libre y la cache para estimar la RAM disponible en el sistema. Esta estimación era correcta , pero con el paso de los años dejó de ser precisa.

Por ello, se modificó el kernel para dar una estimación de la memoria disponible en /proc/meminfo, el fichero en el que el kernel de Linux reporta el estado de la memoria RAM. Esta estimación tiene en cuenta segmentos de memoria compartidos, sistemas de ficheros temporales en RAM (tmpfs/ramfs) y no incluye el espacio que se puede reclamar de la slab cache, un porcentaje importante en algunos sistemas. En el cambio se describen los detalles.

En los siguientes años, las herramientas se actualizaron para utilizar esta estimación. En el caso de free se añadió como la columna available.

Cuando se habla de memoria disponible, se refiere indistintamente al valor de la columna available o la suma de free y cache en sistemas anteriores.

¿Cuándo debería empezar a preocuparme?

Tras ejecutar cierto tiempo, cualquier sistema operando con normalidad y memoria suficiente debería mostrar

  • memoria libre cercana a cero;
  • memoria ocupada cerca del total de memoria instalada en el sistema;
  • memoria disponible (o free + cache en sistemas con el formato viejo) en torno al 20 % o más del total de memoria; y
  • uso de swap sin cambios, preferiblemente cero.

Los principales signos de que el sistema tiene problemas de RAM son

  • memoria disponible (o free + cache) cerca de cero;
  • incremento o fluctuaciones en el uso de swap; o
  • aparecen entradas al ejecutar dmesg | grep oom-killer.

Este último caso, significa que el sistema no tiene la memoria mínima suficiente para funcionar. Para evitar un colapso, está intentando liberarla abortando las aplicaciones según un ranking (habitualmente, las que más RAM están usando).

Experimentando con la cache de disco

Para verificar que esto es verdad, se pueden ejecutar unas pequeñas pruebas para comprobar cómo funciona la cache de disco. Para esta sección se necesitan algunos conocimientos de programación en sistemas Linux.

Las pruebas se han ejecutado en una máquina virtual corriendo Debian 11 con 4 vCPUs, 4 GiB de RAM y 10 GiB de disco SSD servido desde una cabina NAS.

Estos experimentos fueron diseñados para el hardware disponible . En aquel momento lo más habitual eran discos mecánicos y muy pocos GiB de RAM.

Los sistemas modernos mejoran estas prestaciones significativamente. Además, tanto el uso de virtualización como de cabinas de almacenamiento añade capas de optimización (por ejemplo, cache tanto en hipervisor como en la cabina) que incrementan el rendimiento los resultados.

A pesar de todo, siguen siendo apreciables las mejoras que aporta el uso de la memoria RAM descrito. Sin embargo, puede ser necesario ajustar las pruebas en un orden de magnitud para tener un efecto apreciable en el futuro.

Efectos en la asignación de la memoria de la aplicación

En primer lugar, se va a comprobar que la cache de disco no impide que las aplicaciones puedan utilizar la memoria que necesiten. Para verificarlo se va a utilizar una pequeña utilidad programada en C que pide tanta memoria como sea posible o hasta el límite que se le indique:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>

/* 1 MiB = 1024 * 1024 */
#define ALLOC_UNIT 1048576

int main(int argc, char** argv)
{
    register unsigned int max = UINT_MAX;
    if (argc > 1) {
        max = atoi(argv[1]);
    }

    register char* buffer;
    for (register unsigned int mib = 1; mib <= max; mib++) {
        buffer = malloc(ALLOC_UNIT);
        memset(buffer, 0, ALLOC_UNIT);

        printf("Allocated %d MiB\n", mib);
    }

    return 0;
}

Para generar el ejecutable basta con ejecutar:

gcc -o munch munch.c

Ejecutando el programa sin argumentos consumirá toda la memoria disponible. Esto no supone ningún problema ya que el Out of Memory (OOM) Killer detectará la situación y abortará el proceso sin afectar al resto. Sin embargo, es necesario desactivar la swap antes de ejecutar o empezará a swappear hasta agotar todo el espacio reservado y ralentizando el sistema.

Aunque el primer free indicaba que quedaban 2551 MiB libres, no ha supuesto ningún problema para que munch haya utilizado 3499 MiB. Después de la ejecución, la cache está muy vacía (38 MiB) y queda casi toda la RAM libre. Este estado se irá revertiendo poco a poco según se accedan a otros ficheros hasta llegar al estado inicial.

¿No se vacía completamente la cache?

Algunas partes de la cache no se pueden liberar ni siquiera para nuevas aplicaciones. Esto incluye páginas de memoria bloqueadas expresamente por alguna aplicación, bloques de la cache con modificaciones (“sucios”) que aún no se han escrito en el almacenamiento persistente o los datos almacenados en tmpfs (incluyendo /dev/shm).

Esta situación se corregirá con el tiempo y la cache se vaciará en cuanto sea posible:

  • Las páginas bloqueadas permanecerán en ese estado hasta que la aplicación las libere o cuando finalice su ejecución.

  • Los bloques modificados eventualmente se escribirán en el almacenamiento y podrán ser liberados a partir de ese momento.

  • Los datos en tmpfs se escribirán en swap cuando sea necesario.

Regenerando la cache de bloques

Este segundo experimento comprueba que la cache de disco se regenera automáticamente con el tiempo según se va accediendo a ficheros.

Para verificar esto basta con ejecutar watch free -m en un terminal y en otro para ir leyendo ficheros del disco (se puede forzar con find /usr /var -type f -exec cat {} + > /dev/null que lee todos los ficheros de /usr y /var descartando la salida), lo que hará que se almacenen automáticamente en la cache. Mientras se ejecuta se va reduciendo la memoria identificada como free incrementando la destinada a buffers/cached. Pasado un tiempo se estabilizará y en ningún momento se tocará la swap.

Controlando la swap

Aunque la nueva memoria para una aplicación siempre se asignará primero la cache de disco antes que de la swap (con algunas excepciones, como las indicadas en el experimento anterior), se puede configurar para que las páginas de RAM de aplicaciones en segundo plano se vuelquen a swap y usar esa memoria para caches. Este comportamiento se puede configurar a través de /proc/sys/vm/swappiness.

Por ejemplo, un servidor puede querer liberar toda la memoria posible de aplicaciones no utilizadas para acelerar el acceso al disco de las que sí están ejecutando para hacer que el sistema sea más rápido y fluido. Sin embargo, un sistema de escritorio puede preferir mantener las aplicaciones en memoria para evitar retardos cuando el usuario vuelve a ellas, mejorando la respuesta percibida por el usuario.

La configuración depende de las necesidades de cada sistema concreto.

Efectos en la swap

El siguiente paso es comprobar que la cache de disco no hace que las aplicaciones usen la swap. Para ello se utiliza la misma utilidad munch pero con la swap activa y limitando el uso de RAM a unos cuantos MiB.

La aplicación ha podido usar los 2000 MiB de RAM indicados sin necesidad de tocar la swap, aunque únicamente quedaban 1424 MiB libres. Para ello se ha ido reduciendo la cantidad de RAM destinada a buffers/cached. Con el paso del tiempo, la cache de disco se volverá a rellenar y tampoco utilizará swap.

Vaciando la cache

En algunos casos excepcionales es necesario vaciar la cache de disco. Escribiendo el valor 3 en el fichero especial /proc/sys/vm/drop_caches se liberará todo el espacio posible.

Tras vaciar la cache se han reducido principalmente el espacio de RAM destinado a buffers/cache incrementándose el espacio free. La suma de free y buffers/cache es aproximadamente la misma.

Carga de aplicaciones

Hasta ahora se ha comprobado que la memoria destinada a la cache de bloques no afecta en nada a la ejecución de aplicaciones, pero ¿está sirviendo para algo?

Para este experimento se van a utilizar el típico Hello World usando Python y Java, un par de lenguajes que requieren cargar un runtime muy pesado para poder ejecutar. Este es el escenario perfecto para ver el efecto de la cache de disco.

Se va a medir la ejecución de cada programa usando time en dos momentos diferentes:

  • La primera medida se hará justo después de limpiar la cache, por lo que tendrá que cargar prácticamente los ficheros del runtime del disco.

  • La segunda inmediatamente después de la primera ejecución, y se encontrará casi todo el runtime en la cache.

Python

El código de un Hello World de Python es extremadamente sencillo:

print("Hello World! Love, Python")

Como se puede ver la cache de disco ha acelerado la carga del framework es algo más de 3 veces más rápido.

En el artículo original la mejora es un orden de magnitud superior (45 veces más rápido). Esta diferencia es debido a la mejora en el equipamiento de pruebas utilizado.

De hecho, el tiempo de ejecución una vez cacheado es el mismo que se obtuvo , hace más de una década. El uso de la RAM para cache lleva todo este tiempo equiparando el rendimiento a prácticamente el hardware actual.

Java

La siguiente prueba es equivalente pero usando Java, que tiene un runtime incluso más pesado.

class Hello {
    public static void main(String[] args) throws Exception {
        System.out.println("Hello World! Regards, Java");
    }
}

En este caso es necesario compilarlo previamente a código intermedio:

javac Hello.java

Y por último lanzar la prueba

En el caso de Java, la carga es prácticamente 4 veces más rápida.

En el artículo original la mejora de la mejora es de 15 veces, nuevamente debido al uso de otro equipamiento para realizar las pruebas.

Lectura de ficheros

En el caso anterior se ha mejorado sustancialmente la carga de aplicaciones con un runtime pesado. Y no es el único caso en el que la cache está acelerando la ejecución. Cualquier acceso repetido a disco se verá beneficiado por la cache.

Velocidad de acceso

Para verificar esto se va a simular el caso de crear un fichero y leerlo varias veces. Esto sería equivalente a crear un documento, cerrar el editor y volver a abrirlo para hacer un cambio.

  1. El primer caso es crear un “documento”, en este caso un fichero de 200 MiB lleno de ceros.

    Las escrituras también se almacenan en la cache de bloques (como se comentó anteriormente, estos son bloques “sucios” que se escribirán en el almacenamiento de forma optimizada). Esto se ve claramente en el incremento de 200 MiB en buff/cache.

  2. Al haberse almacenado las escrituras en la cache, la lectura siguiente se beneficia y sirve desde la cache. Justo después se vacía la cache y se repite la prueba tardando 6.4 veces más tiempo en la misma lectura.

En el artículo original la mejora de la mejora es de 15 veces. La diferencia se debe, nuevamente, al distinto hardware empleado en ambos casos.

Ancho de banda

Con la prueba anterior se muestra cómo la cache está acelerando el sistema. Esto genera un segundo efecto: la mejora virtual el ancho de banda a disco.

Para verificarlo, se utiliza dd, que proporciona información del tiempo y ancho de banda de las transferencias, con la cache limpia y cargada.

La segunda lectura es prácticamente un orden de magnitud más rápida que la primera: se pasa de leer un disco SSD a 355 MB/s a leerlo a 3 GB/s.

Conclusiones

En la actualidad, la memoria RAM es un recurso físico gestionado por el sistema operativo y puesto a disposición de las aplicaciones en forma de Memoria Virtual. Si dicho recurso no va a ser utilizado para otra finalidad, es mejor destinarlo a cualquier técnica que pueda mejorar el rendimiento y el funcionamiento del sistema antes que desaprovecharlo.

Este es el caso de la cache de disco. Utiliza discretamente la memoria sobrante para mejorar significativamente el rendimiento de acceso (incluso en dispositivos SSD) sin tener ningún tipo de contraprestación como se ha podido verificar en los experimentos. Es un ejemplo de un uso lo más eficiente posible de los recursos disponibles. No debe producir ninguna alarma o intranquilidad.

Por último, agradecer a Vidar Holen, el autor de los artículos originales, su trabajo.


  1. No tiene mucho sentido almacenar información para acelerar el acceso al disco en el propio disco que se pretende acelerar. El acceso tardaría lo mismo y sólo incrementaría la carga del dispositivo pudiendo hacer que responda con mayor lentitud. ↩︎

Oscar Cubo Medina
Oscar Cubo Medina
Ingeniero en informática

Director técnico en CeSViMa. Ciencia, informática, música, libros y nuevas tecnologías.

Relacionado