evitar el congelamiento / falta de respuesta del sistema debido al intercambio de uso de memoria

48

Si un proceso requiere mucha memoria, el sistema mueve todos los demás procesos al archivo de intercambio. Incluyendo lo que parece, procesos necesarios como el servidor X11 o el terminal.

Entonces, si un proceso sigue asignando sin límite, todo deja de responder, hasta que el OOM-killer mata ese proceso. Mi computadora portátil parece ser especialmente sensible y reacciona extremadamente mal. Acabo de pasar una HORA ENTERA esperando la finalización del proceso durante el cual ni siquiera se podía mover el cursor del mouse.

¿Cómo puede esto ser evitado?

1) Deshabilitar el intercambio => A menudo inicio muchos procesos que luego se vuelven inactivos. Los inactivos deben trasladarse al intercambio.

2) Obtenga un SSD => demasiado caro

3) establezca una memoria máxima ulimit => pero luego falla en caso de que un programa necesite una gran cantidad de memoria razonable. el problema no es que usa demasiado, sino que suprime los otros procesos

4) mantenga programas importantes (X11, bash, kill, top, ...) en la memoria y nunca intercambie esos => ¿se puede hacer esto? ¿cómo? tal vez solo intercambiar programas grandes?

5)

BeniBela
fuente
Y sucedió nuevamente :( Comenzó gcc mientras Firefox estaba funcionando, todo estuvo bloqueado durante media hora. Sin movimiento del mouse, sin lectura de libros electrónicos
BeniBela
No estoy seguro de entender lo que quieres. ¿Desea que el sistema haga las cosas mágicamente más rápido con los recursos que tiene? ¿Desea que elimine procesos que usan mucha memoria antes?
David Schwartz el
Parece que lo que realmente necesitas es más RAM. O menos tareas de fondo "inactivas".
Daniel B
2
Puede usar la combinación de teclas Alt + SysRq + F para forzar la ejecución del OOM-killer. Esto puede reducir el tiempo ridículo necesario para esperar a que su sistema abra una consola para que pueda matar un proceso.
hololeap
Corríjame si estoy equivocado pero este problema en realidad parece que el sistema está buscando espacio de intercambio de disco que debería existir pero que no existe (la partición de intercambio es demasiado pequeña). Si no hay suficiente espacio de intercambio, el sistema "golpeará" el disco duro (o SSD) en busca de espacio disponible.
mchid

Respuestas:

45

TL; DR

Corto temporal / respuesta

  • Más fácil : tenga una partición de intercambio más pequeña y evite que el núcleo intente estar a la altura de la mentira de que no hay límite de memoria al ejecutar procesos desde un almacenamiento lento.
    • Con un gran intercambio, el OOM (sin administrador de memoria) no toma medidas lo suficientemente pronto. Por lo general, cuenta de acuerdo con la memoria virtual y, en mi experiencia pasada, no mataba las cosas hasta que se completaba todo el intercambio, de ahí el sistema de rastreo y rastreo ...
  • ¿Necesita un gran cambio para hibernar?
    • Intento / problema : establezca algunos límites (p. Ej. ulimit -v, Verificar , y tal vez establezca un límite rígido o suave utilizando la asopción en limits.conf). Esto solía funcionar lo suficientemente bien, pero gracias a la presentación de WebKit gigacage, muchas aplicaciones gnome ahora esperan espacios de direcciones ilimitados y no se pueden ejecutar.
    • Intentaron / problemática : La política overcommit y la relación es otra manera de intentar gestionar y mitigar este (por ejemplo sysctl vm.overcommit_memory, sysctl vm.overcommit_ratiopero este método no funcionó para mí.
    • Difícil / complicado : intente aplicar una prioridad de cgroup a los procesos más importantes (por ejemplo, ssh), pero esto actualmente parece engorroso para cgroup v1 (con suerte v2 lo hará más fácil) ...

También encontré:

Solución a largo plazo

espere y espere que algunos parches ascendentes entren en núcleos de distribución estables. También esperamos que los proveedores de distribución optimicen mejor los valores predeterminados del kernel y aprovechen mejor los cgroups systemd para priorizar la capacidad de respuesta de la GUI en las ediciones de escritorio.

Algunos parches de interés:

Por lo tanto, no es solo un mal código de espacio de usuario y configuración de distribución / valores predeterminados lo que tiene la culpa: el kernel podría manejar esto mejor.

Comentarios sobre opciones ya consideradas

1) Deshabilitar el intercambio

Se recomienda proporcionar al menos una pequeña partición de intercambio ( ¿Realmente necesitamos intercambio en los sistemas modernos? ). Deshabilitar el intercambio no solo evita el intercambio de páginas no utilizadas, sino que también puede afectar la estrategia de sobrecompromiso heurística predeterminada del núcleo para asignar memoria ( ¿Qué significa la heurística en Overcommit_memory = 0? ), Ya que esa heurística cuenta con páginas de intercambio. Sin intercambio, el exceso de compromiso todavía puede funcionar en los modos heurístico (0) o siempre (1), pero la combinación de ningún intercambio y la estrategia de exceso de compromiso nunca (2) es probablemente una idea terrible. Por lo tanto, en la mayoría de los casos, es probable que ningún intercambio perjudique el rendimiento.

Por ejemplo, piense en un proceso de larga ejecución que inicialmente toca la memoria para el trabajo de una sola vez, pero luego no libera esa memoria y sigue ejecutando el fondo. El núcleo tendrá que usar RAM para eso hasta que finalice el proceso. Sin ningún intercambio, el kernel no puede localizarlo por otra cosa que realmente quiera usar RAM activamente. También piense en cuántos desarrolladores son perezosos y no liberan explícitamente memoria después del uso.

3) establecer una memoria máxima ulimit

¡Solo se aplica por proceso, y probablemente sea una suposición razonable que un proceso no debería solicitar más memoria de la que tiene físicamente un sistema! Por lo tanto, probablemente sea útil evitar que un proceso loco solitario desencadene una paliza mientras se establece generosamente.

4) mantenga programas importantes (X11, bash, kill, top, ...) en la memoria y nunca los cambie

Buena idea, pero esos programas acapararán la memoria que no están usando activamente. Puede ser aceptable si el programa solo solicita una cantidad modesta de memoria.

La versión systemd 232 acaba de agregar algunas opciones que hacen esto posible: creo que uno podría usar 'MemorySwapMax = 0' para evitar que una unidad (servicio) como ssh tenga algo de memoria intercambiada.

Sin embargo, ser capaz de priorizar el acceso a la memoria sería mejor.

Larga explicación

El kernel de Linux está más ajustado para las cargas de trabajo del servidor, por lo que la capacidad de respuesta de la GUI ha sido lamentablemente una preocupación secundaria ... La configuración de administración de memoria del kernel en la edición de escritorio de Ubuntu 16.04 LTS no parecía diferir de las otras ediciones del servidor. Incluso coincide con los valores predeterminados en RHEL / CentOS 7.2 que generalmente se usan como servidor.

OOM, limite e intercambie integridad por capacidad de respuesta

El intercambio de memoria (cuando el conjunto de memoria de trabajo, es decir, las páginas que se leen y escriben en un período de tiempo corto excede la RAM física) siempre bloqueará la E / S de almacenamiento; ninguna herramienta de kernel puede salvar un sistema de esto sin matar un proceso o dos...

Espero que los ajustes de OOM de Linux que se presenten en los núcleos más recientes reconozcan que este conjunto de trabajo excede la situación de la memoria física y mata un proceso. Cuando no es así, ocurre el problema de la paliza. El problema es que, con una gran partición de intercambio, puede parecer que el sistema todavía tiene espacio para la cabeza mientras que el núcleo se compromete alegremente y aún sirve solicitudes de memoria, pero el conjunto de trabajo podría desbordarse, tratando efectivamente de tratar el almacenamiento como si Es RAM.

En los servidores, acepta la penalización de rendimiento de la paliza por un determinado, lento, no perder datos, compensación. En las computadoras de escritorio, la compensación es diferente y los usuarios preferirían un poco de pérdida de datos (sacrificio de proceso) para mantener las cosas receptivas.

Esta fue una buena analogía cómica sobre OOM: oom_pardon, alias no mates mi xlock

Por cierto, OOMScoreAdjustes otra opción del sistema para ayudar al peso y evitar los procesos de destrucción de OOM considerados más importantes.

reescritura amortiguada

Creo que " Hacer que la reescritura en segundo plano no sea mala " ayudará a evitar algunos problemas en los que un proceso que acapara la RAM provoca otro intercambio (escritura en el disco) y la escritura masiva en el disco detiene cualquier otra cosa que desee IO. No es la causa del problema, pero sí contribuye a la degradación general de la capacidad de respuesta.

limitación de ulimits

Un problema con ulimits es que el límite de contabilidad se aplica al espacio de direcciones de la memoria virtual (lo que implica combinar tanto el espacio físico como el de intercambio). Según man limits.conf:

       rss
          maximum resident set size (KB) (Ignored in Linux 2.4.30 and
          higher)

Por lo tanto, configurar un ulimit para que se aplique solo al uso de RAM física ya no parece útil. Por lo tanto

      as
          address space limit (KB)

parece ser el único sintonizable respetado.

Desafortunadamente, como se detalla más en el ejemplo de WebKit / Gnome, algunas aplicaciones no pueden ejecutarse si la asignación de espacio de direcciones virtuales es limitada.

cgroups debería ayudar en el futuro?

Actualmente, parece engorroso, pero es posible habilitar algunos indicadores de cgroup del núcleo cgroup_enable=memory swapaccount=1(por ejemplo, en la configuración de grub) y luego intentar usar el controlador de memoria cgroup para limitar el uso de la memoria.

Los cgroups tienen características de límite de memoria más avanzadas que las opciones 'ulimit'. Las notas de CGroup v2 apuntan a intentos de mejorar el funcionamiento de ulimits.

La contabilidad y la limitación combinadas de memoria + intercambio se reemplazan por un control real sobre el espacio de intercambio.

Las opciones de CGroup se pueden configurar mediante las opciones de control de recursos systemd . P.ej:

  • Memoria alta
  • MemoryMax

Otras opciones útiles pueden ser

  • IOWeight
  • CPUShares

Estos tienen algunos inconvenientes:

  1. Gastos generales. La documentación actual de la ventana acoplable menciona brevemente el 1% de uso de memoria adicional y el 10% de degradación del rendimiento (probablemente con respecto a las operaciones de asignación de memoria, realmente no especifica).
  2. Las cosas de Cgroup / systemd se han reestructurado recientemente, por lo que el flujo ascendente implica que los proveedores de distribución de Linux podrían estar esperando que se solucione primero.

En CGroup v2 , sugieren que memory.highdebería ser una buena opción para regular y administrar el uso de memoria por parte de un grupo de procesos. Sin embargo, esta cita sugiere que monitorear las situaciones de presión de memoria necesitaba más trabajo (a partir de 2015).

Es necesaria una medida de la presión de la memoria (cuánto afecta la carga de trabajo debido a la falta de memoria) para determinar si una carga de trabajo necesita más memoria; desafortunadamente, el mecanismo de monitoreo de presión de memoria aún no está implementado.

Dado que las herramientas de espacio de usuario de systemd y cgroup son complejas, no he encontrado una manera simple de establecer algo apropiado y aprovechar esto aún más. La documentación de cgroup y systemd para Ubuntu no es excelente. El trabajo futuro debería ser para las distribuciones con ediciones de escritorio para aprovechar cgroups y systemd de modo que bajo una alta presión de memoria, ssh y los componentes de X-Server / window manager obtengan mayor prioridad de acceso a CPU, RAM física y IO de almacenamiento, para evitar competir con los procesos intercambio ocupado Las características de prioridad de CPU y E / S del núcleo han existido por un tiempo. Parece que falta acceso prioritario a la RAM física.

Sin embargo, ¡ni siquiera las prioridades de CPU e IO están configuradas adecuadamente! Cuando pude comprobar los límites de cgroup, cpu group, etc., aplicados, por lo que pude ver, Ubuntu no había hecho ninguna priorización predefinida. Por ejemplo, corrí:

systemctl show dev-mapper-Ubuntu\x2dswap.swap

Comparé eso con la misma salida para ssh, samba, gdm y nginx. Cosas importantes como la GUI y la consola de administración remota tienen que luchar por igual con todos los demás procesos cuando se produce la agitación.

Ejemplo de límites de memoria que tengo en un sistema de 16 GB de RAM

Quería habilitar la hibernación, así que necesitaba una gran partición de intercambio. Por lo tanto, intentar mitigar con ulimits, etc.

ulimit

Me puse * hard as 16777216de /etc/security/limits.d/mem.conftal manera que ningún proceso único podría solicitar más memoria de la que es físicamente posible. No evitaré la agitación por completo, pero sin un solo proceso con uso de memoria codicioso, o una pérdida de memoria, puede provocar la agitación. Por ejemplo, he visto gnome-contactsabsorber 8GB + de memoria al hacer cosas mundanas como actualizar la lista global de direcciones desde un servidor de intercambio ...

Contactos de GNOME masticando RAM

Como se ve ulimit -S -v, muchas distribuciones tienen este límite rígido y suave establecido como 'ilimitado' dado, en teoría, un proceso podría terminar solicitando mucha memoria pero solo usando activamente un subconjunto, y correr felizmente pensando que se le ha dado, por ejemplo, 24 GB de RAM mientras El sistema solo tiene 16GB. El límite rígido anterior causará que los procesos que pudieron ejecutarse correctamente se cancelen cuando el núcleo niega sus codiciosas solicitudes de memoria especulativa.

Sin embargo, también detecta cosas locas como contactos de gnomos y, en lugar de perder la capacidad de respuesta de mi escritorio, aparece el error "no hay suficiente memoria libre":

ingrese la descripción de la imagen aquí

Complicaciones que configuran ulimit para el espacio de direcciones (memoria virtual)

Desafortunadamente, a algunos desarrolladores les gusta simular que la memoria virtual es un recurso infinito y establecer un límite en la memoria virtual puede dañar algunas aplicaciones. Por ejemplo, WebKit (del que dependen algunas aplicaciones de gnome) agregó una gigacagecaracterística de seguridad que intenta asignar cantidades increíbles de memoria virtual y ocurren FATAL: Could not allocate gigacage memoryerrores con un toque descarado Make sure you have not set a virtual memory limit. La solución,GIGACAGE_ENABLED=norenuncia a los beneficios de seguridad, pero del mismo modo, no se permite limitar la asignación de memoria virtual también se renuncia a una característica de seguridad (por ejemplo, control de recursos que puede evitar la denegación de servicio). Irónicamente, entre los desarrolladores de gigacage y gnome, parecen olvidar que limitar la asignación de memoria es en sí mismo un control de seguridad. Y lamentablemente, noté que las aplicaciones gnome que dependen de gigacage no se molestan en solicitar explícitamente un límite superior, por lo que incluso un límite suave rompe las cosas en este caso.

Para ser justos, si el núcleo hizo un mejor trabajo al poder negar la asignación de memoria basada en el uso de la memoria residente en lugar de la memoria virtual, fingir que la memoria virtual es ilimitada sería menos peligroso.

exceso de compromiso

Si prefiere que se les niegue el acceso a la memoria a las aplicaciones y desea detener el exceso de compromiso, use los comandos a continuación para probar cómo se comporta su sistema cuando se encuentra bajo una presión de memoria alta.

En mi caso, la relación de compromiso predeterminada era:

$ sysctl vm.overcommit_ratio
vm.overcommit_ratio = 50

Pero solo entra en vigencia cuando se cambia la política para deshabilitar el exceso de compromiso y aplicar la proporción

sudo sysctl -w vm.overcommit_memory=2

La relación implicaba que solo se podían asignar 24GB de memoria en general (16GB RAM * 0.5 + 16GB SWAP). Por lo tanto, probablemente nunca vería aparecer OOM, y sería menos probable que los procesos accedan constantemente a la memoria en el intercambio. Pero también es probable que sacrifique la eficiencia general del sistema.

Esto provocará el bloqueo de muchas aplicaciones, dado que es común que los desarrolladores no manejen con gracia el sistema operativo que rechaza una solicitud de asignación de memoria. Cambia el riesgo ocasional de un bloqueo prolongado debido a la paliza (perder todo su trabajo después del restablecimiento completo) a un riesgo más frecuente de que varias aplicaciones se bloqueen. En mis pruebas, no ayudó mucho porque el escritorio se bloqueó cuando el sistema estaba bajo presión de memoria y no podía asignar memoria. Sin embargo, al menos las consolas y SSH todavía funcionaban.

Cómo funciona la memoria de sobrecompromiso de VM tiene más información.

Elegí volver al valor predeterminado para esto, sudo sysctl -w vm.overcommit_memory=0dada la pila gráfica de escritorio completo y las aplicaciones en él se bloquean.

JPvRiel
fuente
44
Esta es la mejor reseña que he visto sobre el tema, y ​​no recurres a "¡solo compra más RAM!" Una cosa que no menciona es cómo las opciones de configuración del kernel juegan en esto. Por ejemplo, ¿el modelo de preemtion utilizado o el planificador de E / S utilizado tendría algún efecto importante en esto?
hololeap
2
but the kernel won't be able to use an overcommit strategy for allocating memory and this will likely hurt performance. Even a smaller swap partition allows this. ¿Hay alguna prueba de esto? Creo que el kernel se compromete
demasiado
@ygrek, gracias, esa redacción se ve bastante equivocada: el exceso de compromiso todavía funciona sin intercambio, a menos que se elija el modo de nunca exceso de compromiso (2). El modo de sobrecompra heurístico predeterminado (0), o los modos de confirmación (1) probablemente todavía funcionen sin intercambio. No recuerdo dónde leí que desactivar el intercambio perjudica la capacidad del código de sobrecompromiso para asignar memoria (y a qué modo afectó específicamente). Busqué en Google nuevamente sin suerte, pero esta fue una publicación semi útil: stackoverflow.com/questions/38688824/… Editaré la respuesta de manera adecuada.
JPvRiel
Esperaba que el parche de reescritura en segundo plano me ayudara, pero estoy en un kernel muy nuevo (4.14.81) y enfrento problemas de respuesta considerables bajo E / S de disco pesado (independientemente de que el intercambio sea la causa o no). Me pregunto si el administrador de dispositivos o mi cifrado LUKS están causando problemas de rendimiento de alguna manera. Realmente tengo poca idea de cómo depurar este tipo de problema.
Darius Jahandarie