En la salida de atop de un servidor Linux me encontré con esta situación extraña:

Si queda memoria principal disponible y el uso de swap es cero, ¿por qué la fila SWP
figura en color rojo (uso crítico)?
Esto se debe a que el sistema operativo ha entregado a los procesos más memoria de la que dispone. Del manual de atop
(man atop
):
If the committed virtual memory exceeds the limit (`vmcom' and `vmlim' in the SWP-line), the SWP-line is colored due to overcom- mitting the system.
Cuando un sistema operativo asigna memoria a un proceso en el espacio usuario, no existe una correspondencia fija entre las páginas de memoria virtual en el espacio de memoria del proceso y la memoria física de la máquina (frames). De hecho, antes de que la memoria asignada sea utilizada por primera vez, es probable que no exista ninguna correspondencia a memoria física. Sólo luego del primer intento de acceso a memoria el kernel se encarga de reservar el espacio correspondiente en memoria física.
Esto significa que el sistema operativo puede asignar a un proceso una cantidad de memoria prácticamente ilimitada, siempre y cuando éste no intente accederla al mismo tiempo. Overcommit se refiere justamente a esta práctica: asignar memoria virtual sin garantía de que exista almacenamiento físico para la misma. Ya sea memoria principal (RAM) o memoria secundaria (swap o área de intercambio). En este contexto, el sistema operativo continúa entregando memoria virtual hasta que un programa toca una página previamente no tocada y el kernel no puede encontrar memoria física disponible para instanciarla. Así las cosas empiezan a fallar de forma horrible.
En Linux, cuando la memoria física (RAM + swap) se agota, ocurre un evento OOM (Out Of Memory) en el kernel, el cual utiliza un algoritmo para determinar qué proceso es el culpable de agotar la memoria, para luego matar dicho proceso. Esto en la práctica funciona relativamente bien, pues se evitan matar procesos críticos del sistema y se mata el proceso consumiendo demasiada memoria. Pero en realidad ningún proceso es realmente culpable de usar más memoria de la disponible, pues a ninguno se le dijo que la memoria física era insuficiente al momento de reservarla.
La estrategia para evitar el overcommitting consiste en llevar la cuenta de cuanta memoria se ha asignado. Esto es lo que hace Linux cuando se configura el parámetro vm.overcommit_memory
en 2
. De esta forma, toda la memoria que pueda ser potencialmente accesible (ya sea en memoria principal como en swap) cuenta como memoria asignada a procesos y aplicaciones. Cuando una solicitud de memoria de un proceso provoca que la asignación total sobrepase un límite configurado (por defecto el tamaño del área de intercambio más la mitad de la memoria RAM), la solicitud falla.
Desafortunadamente a la mayoría de desarrolladores de aplicaciones les gusta pedir memoria de más (overcommit). Ya sea porque ésto permite contar con cantidades ridículas de memoria (siempre que se haga un uso responsable de la misma), o por pereza al momento de verificar errores (ignorar el código de retorno de malloc
).
Sea como sea, puede ser prácticamente imposible para un SysAdmin controlar el uso de memoria de una aplicación desarrollada por terceros (más allá de que sea de código abierto), por lo que hay que estar atentos ante estas situaciones. En caso de máquinas virtuales, es posible agregar más memoria RAM, pues de todas formas no es memoria que se está utilizando sino que simplemente está reservada. De esta forma sería posible evitar eventuales OOM en el kernel Linux.
Sin embargo, la alternativa correcta sería determinar qué procesos o servicios son responsables por el elevado consumo de memoria (virtual). Para ello se puede recurrir a la herramienta ps
, la cual muestra en su salida el tamaño del espacio de memoria virtual de cada proceso:
root@linuxito:~# ps aux | grep /usr/sbin/mysqld root 6754 0.0 0.1 7732 940 pts/0 S+ 09:42 0:00 grep /usr/sbin/mysqld mysql 23778 0.3 6.3 598152 33312 ? Sl Aug24 55:14 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=**** --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/run/mysqld/mysqld.sock --port=****
La quinta columna describe el tamaño del espacio de memoria virtual del proceso (VSZ). De acuerdo al manual de ps
:
vsz VSZ virtual memory size of the process in KiB (1024-byte units).
Luego será necesario ajustar la configuración del proceso o servicio acaparando el uso de memoria (dentro de lo que el servicio permita y sea factible para que funcione correctamente) revisando cuidadosamente su documentación, tal como por ejemplo expliqué en mi artículo Tunning de InnoDB para reducir al mínimo el consumo de memoria.
Otra forma de examinar detalladamente el consumo de memoria de un proceso puede ser recurriendo a la herramienta pmap
, la cual muestra el mapeo de memoria de un proceso:
root@linuxito:~# pmap -x 23778 23778: /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=**** --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/run/mysqld/mysqld.sock --port=**** Address Kbytes RSS Dirty Mode Mapping 00007f9944000000 11208 1548 1548 rw--- [ anon ] 00007f9944af2000 54328 0 0 ----- [ anon ] 00007f9948000000 3496 84 84 rw--- [ anon ] 00007f994836a000 62040 0 0 ----- [ anon ] 00007f994c000000 2096 24 24 rw--- [ anon ] 00007f994c20c000 63440 0 0 ----- [ anon ] 00007f9950000000 2364 128 128 rw--- [ anon ] 00007f995024f000 63172 0 0 ----- [ anon ] 00007f9954000000 8484 1708 1708 rw--- [ anon ] 00007f9954849000 57052 0 0 ----- [ anon ] 00007f9959706000 4 0 0 ----- [ anon ] 00007f9959707000 192 0 0 rw--- [ anon ] 00007f9959737000 4 0 0 ----- [ anon ] 00007f9959738000 192 0 0 rw--- [ anon ] 00007f9959768000 4 0 0 ----- [ anon ] 00007f9959769000 192 0 0 rw--- [ anon ] 00007f9959799000 4 0 0 ----- [ anon ] 00007f995979a000 192 0 0 rw--- [ anon ] 00007f99597ca000 4 0 0 ----- [ anon ] 00007f99597cb000 192 0 0 rw--- [ anon ] 00007f99597fb000 4 0 0 ----- [ anon ] 00007f99597fc000 192 0 0 rw--- [ anon ] 00007f995982c000 4 0 0 ----- [ anon ] 00007f995982d000 192 60 60 rw--- [ anon ] 00007f995985d000 4 0 0 ----- [ anon ] 00007f995985e000 192 0 0 rw--- [ anon ] 00007f995988e000 4 0 0 ----- [ anon ] 00007f995988f000 192 60 60 rw--- [ anon ] 00007f99598bf000 4 0 0 ----- [ anon ] 00007f99598c0000 192 60 60 rw--- [ anon ] 00007f99598f0000 4 0 0 ----- [ anon ] 00007f99598f1000 192 0 0 rw--- [ anon ] 00007f9959921000 4 0 0 ----- [ anon ] 00007f9959922000 192 0 0 rw--- [ anon ] 00007f9959952000 4 0 0 ----- [ anon ] 00007f9959953000 192 0 0 rw--- [ anon ] 00007f9959983000 4 0 0 ----- [ anon ] 00007f9959984000 192 0 0 rw--- [ anon ] 00007f99599b4000 4 0 0 ----- [ anon ] 00007f99599b5000 192 0 0 rw--- [ anon ] 00007f99599e5000 4 0 0 ----- [ anon ] 00007f99599e6000 192 0 0 rw--- [ anon ] 00007f9959a16000 4 0 0 ----- [ anon ] 00007f9959a17000 192 0 0 rw--- [ anon ] 00007f9959a47000 4 0 0 ----- [ anon ] 00007f9959a48000 192 0 0 rw--- [ anon ] 00007f9959a78000 4 0 0 ----- [ anon ] 00007f9959a79000 192 0 0 rw--- [ anon ] 00007f9959aa9000 4 0 0 ----- [ anon ] 00007f9959aaa000 192 0 0 rw--- [ anon ] 00007f9959ada000 4 0 0 ----- [ anon ] 00007f9959adb000 192 0 0 rw--- [ anon ] 00007f9959b0b000 4 0 0 ----- [ anon ] 00007f9959b0c000 192 0 0 rw--- [ anon ] 00007f9959b3c000 4 0 0 ----- [ anon ] 00007f9959b3d000 192 0 0 rw--- [ anon ] 00007f9959b6d000 4 0 0 ----- [ anon ] 00007f9959b6e000 192 0 0 rw--- [ anon ] 00007f9959b9e000 4 0 0 ----- [ anon ] 00007f9959b9f000 192 0 0 rw--- [ anon ] 00007f9959bcf000 4 0 0 ----- [ anon ] 00007f9959bd0000 192 0 0 rw--- [ anon ] 00007f9959c00000 4 0 0 ----- [ anon ] 00007f9959c01000 192 0 0 rw--- [ anon ] 00007f9959c31000 4 0 0 ----- [ anon ] 00007f9959c32000 192 0 0 rw--- [ anon ] 00007f9959c62000 4 0 0 ----- [ anon ] 00007f9959c63000 10240 16 16 rw--- [ anon ] 00007f995a663000 4 0 0 ----- [ anon ] 00007f995a664000 10240 8 8 rw--- [ anon ] 00007f995b064000 4 0 0 ----- [ anon ] 00007f995b065000 10240 8 8 rw--- [ anon ] 00007f995ba65000 4 0 0 ----- [ anon ] 00007f995ba66000 10240 8 8 rw--- [ anon ] 00007f995c466000 4 0 0 ----- [ anon ] 00007f995c467000 12308 752 752 rw--- [ anon ] 00007f995d083000 4 0 0 ----- [ anon ] 00007f995d084000 192 0 0 rw--- [ anon ] 00007f995d0b4000 4 0 0 ----- [ anon ] 00007f995d0b5000 192 60 60 rw--- [ anon ] 00007f995d0e5000 4 0 0 ----- [ anon ] 00007f995d0e6000 192 0 0 rw--- [ anon ] 00007f995d116000 4 0 0 ----- [ anon ] 00007f995d117000 192 0 0 rw--- [ anon ] 00007f995d147000 4 0 0 ----- [ anon ] 00007f995d148000 192 0 0 rw--- [ anon ] 00007f995d178000 4 0 0 ----- [ anon ] 00007f995d179000 192 0 0 rw--- [ anon ] 00007f995d1a9000 4 0 0 ----- [ anon ] 00007f995d1aa000 192 60 60 rw--- [ anon ] 00007f995d1da000 4 0 0 ----- [ anon ] 00007f995d1db000 192 0 0 rw--- [ anon ] 00007f995d20b000 4 0 0 ----- [ anon ] 00007f995d20c000 192 0 0 rw--- [ anon ] 00007f995d23c000 4 0 0 ----- [ anon ] 00007f995d23d000 192 0 0 rw--- [ anon ] 00007f995d26d000 4 0 0 ----- [ anon ] 00007f995d26e000 10240 8 8 rw--- [ anon ] 00007f995dc6e000 4 0 0 ----- [ anon ] 00007f995dc6f000 10240 8 8 rw--- [ anon ] 00007f995e66f000 4 0 0 ----- [ anon ] 00007f995e670000 10240 8 8 rw--- [ anon ] 00007f995f070000 4 0 0 ----- [ anon ] 00007f995f071000 10240 8 8 rw--- [ anon ] 00007f995fa71000 4 0 0 ----- [ anon ] 00007f995fa72000 10240 4 4 rw--- [ anon ] 00007f9960472000 4 0 0 ----- [ anon ] 00007f9960473000 10240 4 4 rw--- [ anon ] 00007f9960e73000 4 0 0 ----- [ anon ] 00007f9960e74000 10240 4 4 rw--- [ anon ] 00007f9961874000 4 0 0 ----- [ anon ] 00007f9961875000 10240 12 12 rw--- [ anon ] 00007f9962275000 4 0 0 ----- [ anon ] 00007f9962276000 55200 21860 21860 rw--- [ anon ] 00007f996585e000 44 0 0 r-x-- libnss_files-2.13.so 00007f9965869000 2044 0 0 ----- libnss_files-2.13.so 00007f9965a68000 4 0 0 r---- libnss_files-2.13.so 00007f9965a69000 4 0 0 rw--- libnss_files-2.13.so 00007f9965a6a000 40 0 0 r-x-- libnss_nis-2.13.so 00007f9965a74000 2044 0 0 ----- libnss_nis-2.13.so 00007f9965c73000 4 0 0 r---- libnss_nis-2.13.so 00007f9965c74000 4 0 0 rw--- libnss_nis-2.13.so 00007f9965c75000 28 8 0 r-x-- libnss_compat-2.13.so 00007f9965c7c000 2044 0 0 ----- libnss_compat-2.13.so 00007f9965e7b000 4 0 0 r---- libnss_compat-2.13.so 00007f9965e7c000 4 0 0 rw--- libnss_compat-2.13.so 00007f9965e7d000 4 0 0 ----- [ anon ] 00007f9965e7e000 10240 4 4 rw--- [ anon ] 00007f996687e000 84 4 0 r-x-- libnsl-2.13.so 00007f9966893000 2044 0 0 ----- libnsl-2.13.so 00007f9966a92000 4 0 0 r---- libnsl-2.13.so 00007f9966a93000 4 0 0 rw--- libnsl-2.13.so 00007f9966a94000 8 0 0 rw--- [ anon ] 00007f9966a96000 1552 444 0 r-x-- libc-2.13.so 00007f9966c1a000 2044 0 0 ----- libc-2.13.so 00007f9966e19000 16 4 4 r---- libc-2.13.so 00007f9966e1d000 4 4 4 rw--- libc-2.13.so 00007f9966e1e000 20 12 12 rw--- [ anon ] 00007f9966e23000 84 12 0 r-x-- libgcc_s.so.1 00007f9966e38000 2048 0 0 ----- libgcc_s.so.1 00007f9967038000 4 0 0 rw--- libgcc_s.so.1 00007f9967039000 516 16 0 r-x-- libm-2.13.so 00007f99670ba000 2044 0 0 ----- libm-2.13.so 00007f99672b9000 4 0 0 r---- libm-2.13.so 00007f99672ba000 4 0 0 rw--- libm-2.13.so 00007f99672bb000 928 16 0 r-x-- libstdc++.so.6.0.17 00007f99673a3000 2048 0 0 ----- libstdc++.so.6.0.17 00007f99675a3000 32 0 0 r---- libstdc++.so.6.0.17 00007f99675ab000 8 4 4 rw--- libstdc++.so.6.0.17 00007f99675ad000 84 0 0 rw--- [ anon ] 00007f99675c2000 8 4 0 r-x-- libdl-2.13.so 00007f99675c4000 2048 0 0 ----- libdl-2.13.so 00007f99677c4000 4 0 0 r---- libdl-2.13.so 00007f99677c5000 4 0 0 rw--- libdl-2.13.so 00007f99677c6000 32 4 0 r-x-- libcrypt-2.13.so 00007f99677ce000 2044 0 0 ----- libcrypt-2.13.so 00007f99679cd000 4 0 0 r---- libcrypt-2.13.so 00007f99679ce000 4 0 0 rw--- libcrypt-2.13.so 00007f99679cf000 184 0 0 rw--- [ anon ] 00007f99679fd000 36 0 0 r-x-- libwrap.so.0.7.6 00007f9967a06000 2044 0 0 ----- libwrap.so.0.7.6 00007f9967c05000 4 0 0 r---- libwrap.so.0.7.6 00007f9967c06000 4 0 0 rw--- libwrap.so.0.7.6 00007f9967c07000 4 0 0 rw--- [ anon ] 00007f9967c08000 28 8 0 r-x-- librt-2.13.so 00007f9967c0f000 2044 0 0 ----- librt-2.13.so 00007f9967e0e000 4 4 4 r---- librt-2.13.so 00007f9967e0f000 4 0 0 rw--- librt-2.13.so 00007f9967e10000 88 8 0 r-x-- libz.so.1.2.7 00007f9967e26000 2044 0 0 ----- libz.so.1.2.7 00007f9968025000 4 0 0 r---- libz.so.1.2.7 00007f9968026000 4 0 0 rw--- libz.so.1.2.7 00007f9968027000 4 4 0 r-x-- libaio.so.1.0.1 00007f9968028000 2044 0 0 ----- libaio.so.1.0.1 00007f9968227000 4 0 0 r---- libaio.so.1.0.1 00007f9968228000 4 0 0 rw--- libaio.so.1.0.1 00007f9968229000 92 52 0 r-x-- libpthread-2.13.so 00007f9968240000 2044 0 0 ----- libpthread-2.13.so 00007f996843f000 4 4 4 r---- libpthread-2.13.so 00007f9968440000 4 4 4 rw--- libpthread-2.13.so 00007f9968441000 16 4 4 rw--- [ anon ] 00007f9968445000 128 44 0 r-x-- ld-2.13.so 00007f996846b000 4 0 0 ----- [ anon ] 00007f996846c000 192 60 60 rw--- [ anon ] 00007f996849c000 4 0 0 ----- [ anon ] 00007f996849d000 192 60 60 rw--- [ anon ] 00007f99684cd000 4 0 0 ----- [ anon ] 00007f99684ce000 192 0 0 rw--- [ anon ] 00007f99684fe000 4 0 0 ----- [ anon ] 00007f99684ff000 192 0 0 rw--- [ anon ] 00007f996852f000 4 0 0 ----- [ anon ] 00007f9968530000 192 60 60 rw--- [ anon ] 00007f9968560000 4 0 0 ----- [ anon ] 00007f9968561000 192 0 0 rw--- [ anon ] 00007f9968591000 4 0 0 ----- [ anon ] 00007f9968592000 192 0 0 rw--- [ anon ] 00007f99685c2000 4 0 0 ----- [ anon ] 00007f99685c3000 192 0 0 rw--- [ anon ] 00007f99685f3000 4 0 0 ----- [ anon ] 00007f99685f4000 192 0 0 rw--- [ anon ] 00007f9968624000 4 0 0 ----- [ anon ] 00007f9968625000 224 4 4 rw--- [ anon ] 00007f9968661000 12 0 0 rw--- [ anon ] 00007f9968664000 4 4 4 r---- ld-2.13.so 00007f9968665000 4 0 0 rw--- ld-2.13.so 00007f9968666000 4 0 0 rw--- [ anon ] 00007f9968667000 10732 3176 0 r-x-- mysqld 00007f99692e2000 460 196 196 r---- mysqld 00007f9969355000 660 204 184 rw--- mysqld 00007f99693fa000 160 88 88 rw--- [ anon ] 00007f9969ba3000 13372 3156 3156 rw--- [ anon ] 00007ffcdb217000 84 12 12 rw--- [ stack ] 00007ffcdb29c000 8 4 0 r-x-- [ anon ] ffffffffff600000 4 0 0 r-x-- [ anon ] ---------------- ------ ------ ------ total kB 598152 34192 30368
Con la opción -x
se puede obtener la suma del espacio de memoria total, residente y dirty (páginas modificadas en memoria principal).
Al igual que en la salida de ps
, RSS indica la cantidad de memoria residente, esto es, el tamaño que ocupan las páginas almacenadas en memoria física (frames).
Sino es posible acceder directamente al sistema de archivos /proc
, que exporta estructuras de datos del kernel. Dentro del directorio correspondiente a cada proceso existe el archivo status
, el cual contiene información de estado del proceso incluyendo el uso de memoria virtual, memoria física (principal) y secundaria (swap):
root@linuxito:~# grep Vm /proc/23778/status VmPeak: 598152 kB VmSize: 598152 kB VmLck: 0 kB VmHWM: 60908 kB VmRSS: 34436 kB VmData: 551680 kB VmStk: 88 kB VmExe: 10732 kB VmLib: 3692 kB VmPTE: 360 kB VmPTD: 16 kB VmSwap: 36588 kB
Se observa que este proceso tiene la no despreciable cifra de casi 36 MB en memoria secundaria, lo cual puede ser malo para su rendimiento.
Referencias
man atop man ps man pmap
What is Overcommit? And why is it bad?