Cómo solucionar problemas de memoria

Kayce Basques
Kayce Basques

Aprende a usar Chrome y las Herramientas para desarrolladores a fin de encontrar problemas de memoria que afecten el rendimiento de la página, como fugas o aumentos de memoria y recolecciones frecuentes de elementos no utilizados.

Resumen

  • Descubre cuánta memoria usa tu página actualmente con el Administrador de tareas de Chrome.
  • Visualizar el uso de memoria en el tiempo con grabaciones de Rutas
  • Identifica árboles separados del DOM (una causa común de fugas de memoria) con capturas de pantalla del montón.
  • Averigua cuándo se asigna memoria nueva a tu montón JS con grabaciones de líneas de tiempo de asignación.

Descripción general

Según el espíritu del modelo de rendimiento RAIL, el enfoque de tus esfuerzos de rendimiento deben ser tus usuarios.

Los problemas de memoria son importantes porque, a menudo, los usuarios los pueden percibir. Los usuarios pueden percibir los problemas de memoria de las siguientes maneras:

  • El rendimiento de una página empeora progresivamente con el tiempo. Esto posiblemente sea un síntoma de una fuga de memoria. Una fuga de memoria se produce cuando un error en la página hace que, con el tiempo, esta use cada vez más memoria.
  • El rendimiento de una página es siempre bajo. Es posible que esto sea un síntoma de un aumento de la memoria. Un aumento de memoria se produce cuando una página usa más memoria de la necesaria para lograr una velocidad óptima.
  • El rendimiento de una página se retrasa o parece detenerse con frecuencia. Esto puede ser un síntoma de recolecciones frecuentes de elementos no utilizados. La recolección de elementos no utilizados se produce cuando el navegador reclama memoria. El navegador decide cuándo sucede esto. Durante la recopilación, la ejecución de todas las secuencias de comandos se detiene. Por lo tanto, si el navegador realiza muchas recolecciones de elementos no utilizados, se detendrá mucho la ejecución de la secuencia de comandos.

Aumento de memoria: ¿cuánto es "demasiado"?

Una fuga de memoria es fácil de definir. Si un sitio usa cada vez más memoria de forma progresiva, significa que hay una fuga. Pero el sobredimensionamiento de memoria es un poco más difícil de identificar. ¿Qué califica como “usar demasiada memoria”?

No hay números estrictos aquí, ya que los distintos dispositivos y navegadores tienen capacidades diferentes. La misma página que se ejecuta sin problemas en un smartphone de alta gama puede fallar en uno de gama baja.

La clave aquí es usar el modelo RAIL y centrarse en los usuarios. Descubre qué dispositivos son populares entre tus usuarios y, luego, prueba tu página en esos dispositivos. Si la experiencia es mala constantemente, es posible que la página exceda las capacidades de memoria de esos dispositivos.

Supervisa el uso de la memoria en tiempo real con el Administrador de tareas de Chrome

Usa el Administrador de tareas de Chrome como punto de partida para investigar los problemas de memoria. El Administrador de tareas es un monitor en tiempo real que indica cuánta memoria usa una página en el momento.

  1. Presiona Mayúsculas + Esc o ve al menú principal de Chrome y selecciona Más herramientas > Administrador de tareas para abrir el Administrador de tareas.

    Cómo abrir el Administrador de tareas

  2. Haz clic con el botón derecho en el encabezado de la tabla del Administrador de tareas y habilita la memoria de JavaScript.

    Habilita la memoria de JS

Estas dos columnas te brindan información diferente sobre cómo usa la memoria tu página:

  • La columna Memory representa la memoria nativa. Los nodos del DOM se almacenan en la memoria nativa. Si este valor aumenta, significa que se están creando nodos del DOM.
  • La columna JavaScript Memory representa el montón de JS. Esta columna contiene dos valores. El valor que te interesa es el número en vivo (el número entre paréntesis). El número en tiempo real representa la cantidad de memoria que usan los objetos accesibles de tu página. Si este número aumenta, significa que se crean objetos nuevos o que aumenta el número de objetos existentes.

Cómo visualizar fugas de memoria con grabaciones de rendimiento

También puedes usar el panel Rendimiento como otro punto de partida en tu investigación. El panel Performance te ayuda a visualizar el uso de memoria de una página a lo largo del tiempo.

  1. Abre el panel Rendimiento en Herramientas para desarrolladores.
  2. Habilita la casilla de verificación Memory.
  3. Realiza una grabación.

Para demostrar las grabaciones de memoria de rendimiento, ten en cuenta el siguiente código:

var x = [];

function grow() {
  for (var i = 0; i < 10000; i++) {
    document.body.appendChild(document.createElement('div'));
  }
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

Cada vez que se presiona el botón al que se hace referencia en el código, se agregan diez mil nodos div al cuerpo del documento y se envía una cadena de un millón de caracteres x al array x. Cuando se ejecuta este código, se produce una grabación en Rutas como la que se muestra en la siguiente captura de pantalla:

ejemplo de crecimiento simple

Primero, una explicación de la interfaz de usuario. El gráfico HEAP en el panel Overview (debajo de NET) representa el montón de JS. Debajo del panel Descripción general, se encuentra el panel Contador. Aquí puedes ver el uso de memoria desglosado por montón de JS (al igual que el gráfico HEAP del panel Overview), documentos, nodos del DOM, objetos de escucha y memoria de GPU. Si inhabilitas una casilla de verificación, se oculta del gráfico.

Ahora, un análisis del código comparado con la captura de pantalla. Si observas el contador de nodos (el gráfico verde), puedes ver que coincide de forma clara con el código. El recuento de nodos aumenta en pasos discretos. Puedes suponer que cada aumento en el recuento de nodos es una llamada a grow(). El gráfico del montón de JS (el gráfico azul) no es tan sencillo. De acuerdo con las prácticas recomendadas, la primera caída en realidad es una recolección forzada de elementos no utilizados (que se logra presionando el botón recopilar elementos no utilizados). A medida que el registro avanza, puedes ver que el tamaño del montón de JS aumenta repentinamente. Esto es natural y esperado: el código JavaScript crea los nodos del DOM con cada clic en un botón y realiza mucho trabajo cuando crea la cadena de un millón de caracteres. El aspecto clave aquí es el hecho de que el montón JS termina más arriba de lo que empezó (en este caso, el "comienzo" es el punto después de la recolección forzada de elementos no utilizados). En el mundo real, si vieras este patrón de aumento del tamaño del montón de JS o de los nodos, podría significar una fuga de memoria.

Descubre fugas de memoria de árboles separados del DOM con instantáneas del montón

Un nodo del DOM solo puede ser recolección de elementos no utilizados cuando no hay referencias a él en el árbol del DOM de la página ni en el código JavaScript. Se dice que un nodo está "desvinculado" cuando se quita del árbol del DOM, pero JavaScript todavía hace referencia a él. Los nodos del DOM separados son una causa común de fugas de memoria. En esta sección, se explica cómo usar los generadores de perfiles de montón de Herramientas para desarrolladores para identificar nodos desconectados.

Este es un ejemplo sencillo de nodos del DOM separados.

var detachedTree;

function create() {
  var ul = document.createElement('ul');
  for (var i = 0; i < 10; i++) {
    var li = document.createElement('li');
    ul.appendChild(li);
  }
  detachedTree = ul;
}

document.getElementById('create').addEventListener('click', create);

Si haces clic en el botón al que se hace referencia en el código, se crea un nodo ul con diez elementos secundarios li. El código hace referencia a estos nodos, pero no existen en el árbol del DOM, por lo que están separados.

Las instantáneas del montón son una forma de identificar los nodos separados. Como su nombre lo indica, las instantáneas de montón te muestran la distribución de la memoria entre los objetos JS de la página y los nodos del DOM en el momento de la instantánea.

Para crear una instantánea, abre Herramientas para desarrolladores y ve al panel Memory, selecciona el botón de selección Heap Snapshot y, luego, presiona el botón Take Snapshot.

tomar una instantánea del montón

Es posible que la instantánea tarde un poco en procesarse y cargarse. Una vez que termines, selecciónalo en el panel izquierdo (llamado HEAP SNAPSHOTS).

Escribe Detached en el cuadro de texto Class filter para buscar árboles del DOM separados.

filtrado para nodos separados

Expande los quilates para investigar un árbol separado.

investigando un árbol separado

Los nodos destacados en amarillo tienen referencias directas a ellos desde el código JavaScript. Los nodos destacados en rojo no tienen referencias directas. Solo están vivos porque son parte del árbol del nodo amarillo. En general, debes enfocarte en los nodos amarillos. Corrige el código para que el nodo amarillo no esté activo por más tiempo del necesario y también elimina los nodos rojos que forman parte del árbol del nodo amarillo.

Haz clic en un nodo amarillo para investigarlo más a fondo. En el panel Objetos, puedes ver más información sobre el código que hace referencia a él. Por ejemplo, en la siguiente captura de pantalla, puedes ver que la variable detachedTree hace referencia al nodo. Para corregir esta fuga de memoria en particular, debes estudiar el código que usa detachedTree y asegurarte de que quite su referencia al nodo cuando ya no sea necesario.

investigar un nodo amarillo

Identifica fugas de memoria del montón de JS con líneas de tiempo de asignación

El cronograma de asignación es otra herramienta que puede ayudarte a rastrear fugas de memoria en el montón JS.

Para demostrar el cronograma de asignación, ten en cuenta el siguiente código:

var x = [];

function grow() {
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

Cada vez que se presiona el botón al que se hace referencia en el código, se agrega una cadena de un millón de caracteres al array x.

Para grabar una línea de tiempo de asignación, abre Herramientas para desarrolladores, ve al panel Profiles, elige el botón de selección Record Allocation Rutas, presiona el botón Start, realiza la acción que creas que está causando la fuga de memoria y, luego, presiona el botón Stop recording (botón para detener la grabación) cuando hayas terminado.

Mientras grabas, observa si aparecen barras azules en el cronograma de asignación, como en la siguiente captura de pantalla.

asignaciones nuevas

Las barras azules representan asignaciones de memoria nuevas. Esas asignaciones nuevas son tus candidatas a las fugas de memoria. Puedes hacer zoom en una barra para filtrar el panel Constructor de modo que solo muestre los objetos que se asignaron durante el período especificado.

cronograma de asignación ampliado

Expande el objeto y haz clic en su valor para ver más detalles en el panel Objeto. Por ejemplo, en la siguiente captura de pantalla, si ves los detalles del objeto que se asignó recientemente, podrás ver que se asignó a la variable x en el alcance Window.

detalles del objeto

Investiga la asignación de memoria por función

Usa el tipo Allocation Sampling en el panel Memory para ver la asignación de memoria por función de JavaScript.

Generador de perfiles de asignación de registros

  1. Elige el botón de selección Allocation Sampling. Si hay un trabajador en la página, puedes seleccionarlo como el objetivo de generación de perfiles mediante el menú desplegable junto al botón Start.
  2. Presiona el botón Start.
  3. Realiza las acciones en la página que deseas investigar.
  4. Presiona el botón Detener cuando hayas finalizado todas las acciones.

Las Herramientas para desarrolladores te muestran un desglose de la asignación de memoria por función. La vista predeterminada es Heavy (Bottom Up), que muestra las funciones que asignaron más memoria en la parte superior.

Perfil de asignación

Identifica recolecciones frecuentes de elementos no utilizados

Si tu página parece detenerse con frecuencia, es posible que tengas problemas de recolección de elementos no utilizados.

Puedes usar el Administrador de tareas de Chrome o las grabaciones de memoria de Rutas para detectar recolecciones frecuentes de elementos no utilizados. En el Administrador de tareas, los valores de Memory o JavaScript Memory que aumentan y disminuyen con frecuencia representan recolecciones frecuentes de elementos no utilizados. En las grabaciones de Rutas, los gráficos del montón de JS o de recuento de nodos que aumentan y disminuyen con frecuencia indican que hay recolecciones frecuentes de elementos no utilizados.

Una vez que hayas identificado el problema, puedes usar la grabación de un cronograma de asignación para averiguar dónde se asigna la memoria y qué funciones están causando las asignaciones.