Cómo solucionar problemas de memoria

Kayce vascos
Kayce Vascos

Obtén información sobre cómo usar Chrome y las Herramientas para desarrolladores a fin de encontrar problemas de memoria que afecten el rendimiento de la página, como fugas, sobredimensionamiento y recolección frecuente de elementos no utilizados.

Resumen

  • Descubre el volumen de memoria que consume tu página actualmente con el Administrador de tareas de Chrome.
  • Visualiza el uso de la memoria en el tiempo con grabaciones de Rutas.
  • Identifica árboles separados del DOM (una causa común de fugas de memoria) con instantáneas del montón.
  • Averigua el momento en que se asigna memoria nueva a tu montón JS con grabaciones del cronograma 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 los usuarios suelen percibirlos. Los usuarios pueden percibir los problemas de memoria de las siguientes maneras:

  • El rendimiento de una página empeora progresivamente con el tiempo. Esto es posible que sea un síntoma de una fuga de memoria. Una fuga de memoria se produce cuando un error en la página hace que esta use, de manera progresiva, cada vez más memoria con el tiempo.
  • El rendimiento de una página siempre es deficiente. Esto es posible que sea un síntoma de un aumento de memoria. El aumento de memoria ocurre cuando una página usa más memoria de la necesaria para obtener 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 ocurre cuando el navegador recupera memoria. El navegador decide cuándo sucede. Durante la recopilación, se pausa toda la ejecución de secuencias de comandos. Por lo tanto, si el navegador realiza una recolección excesiva de elementos no utilizados, se pausará 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 manera progresiva, significa que existe una fuga. Pero el sobredimensionamiento de la memoria es un poco más difícil de precisar. ¿Qué califica como “usar demasiada memoria”?

No se aplican números estrictos a esto, debido a 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 ellos. Si la experiencia es mala, es posible que la página supere 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 te indica cuánta memoria está usando una página actualmente.

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

    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.

    Habilitar la memoria de JS

Estas dos columnas te brindan información diferente sobre la manera en la que tu página usa la memoria:

  • 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 los nodos del DOM.
  • La columna JavaScript Memory representa el montón JS. Esta columna contiene dos valores. El valor que te interesa es el número en tiempo real (el número entre paréntesis). La cifra en tiempo real representa cuánta memoria usan los objetos accesibles de tu página. Si este número aumenta, significa que se crean objetos nuevos o que los existentes están creciendo.

Cómo visualizar fugas de memoria con grabaciones de rendimiento

También puedes usar el panel Rendimiento como otro punto de partida para tu investigación. El Panel de rendimiento 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 Memoria.
  3. Crea una grabación.

Para demostrar las grabaciones de memoria de rendimiento, considera 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 string de un millón de caracteres x al array x. La ejecución de este código produce una grabación en Timeline como la de 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 JS. Debajo del panel Overview, se encuentra el panel Counter. Aquí puedes ver el uso de memoria desglosado por montón de JS (al igual que el gráfico HEAP en el panel Overview), documentos, nodos del DOM, objetos de escucha y memoria de GPU. Si inhabilitas una casilla de verificación, se ocultará del gráfico.

Ahora, veamos 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 disminución es, en realidad, una recolección forzada de elementos no utilizados (que se logra presionando el botón recopilar elementos no utilizados). A medida que la grabación avanza, puedes ver que el tamaño del montón de JS aumenta repentinamente. Esto es natural y esperable: el código JavaScript crea los nodos del DOM en 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 de JS termina más arriba de lo que comenzó (el "comienzo" aquí es el punto después de la recolección forzada de elementos no utilizados). En el mundo real, si veías este patrón de aumentar el tamaño del montón de JS o del nodo, posiblemente signifique 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 estar sujeto a la recolección de elementos no utilizados cuando no hay referencias a él en el árbol del DOM o el código JavaScript de la página. Se dice que un nodo está "desconectado" cuando se quita del árbol del DOM, pero JavaScript todavía hace referencia a él. Los nodos separados del DOM son una causa común de fugas de memoria. En esta sección, aprenderás a usar los generadores de perfiles de montón de Herramientas para desarrolladores para identificar nodos separados.

A continuación, se muestra un ejemplo simple de nodos separados del DOM.

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);

Cuando haces clic en el botón al que se hace referencia en el código, se crea un nodo ul con diez li secundarios. 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 del 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 tomar 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 instantánea del montón

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

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

filtrado de nodos desconectados

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, se recomienda que te enfoques en los nodos amarillos. Corrige el código para que el nodo amarillo no exista por más tiempo del necesario. Además, también deberías deshacerte de 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 solucionar esta fuga de memoria en particular, deberías estudiar el código que usa detachedTree y asegurarte de que quite su referencia al nodo cuando ya no sea necesaria.

investigando 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 tu 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 envía el botón al que se hace referencia en el código, se agrega una string de un millón de caracteres al array x.

Para grabar un cronograma de asignación, abre Herramientas para desarrolladores, ve al panel Profiles, selecciona el botón de selección Record Allocation Timeline, presiona el botón Start, realiza la acción que sospechas que está causando la fuga de memoria y, luego, presiona el botón de detener la grabación (botón para detener la grabación) cuando hayas terminado.

Mientras realizas la grabación, 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 de memoria nuevas son tus candidatas para fugas de memoria. Puedes hacer zoom en una barra para filtrar el panel Constructor y mostrar solo 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 observas 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. Selecciona 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 quieras investigar.
  4. Presiona el botón Detener cuando hayas terminado todas tus acciones.

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

Perfil de asignación

Identifica recolecciones frecuentes de elementos no utilizados

Si tu página parece pausarse con frecuencia, es posible que tengas problemas con la 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 Timeline, 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 un registro del cronograma de asignación para averiguar dónde se asigna la memoria y qué funciones generan las asignaciones.