Supervisa el uso total de memoria de tu página web con MeasureUserAgentSpecificMemory()

Obtén información para medir el uso de memoria de tu página web en producción para detectar regresiones.

Brendan Kenny
Brendan Kenny
Ulan Degenbaev
Ulan Degenbaev

Los navegadores administran la memoria de las páginas web automáticamente. Cuando una página web crea un objeto, el navegador asigna un fragmento de memoria “detrás de escena” a almacenar el objeto. Como la memoria es un recurso finito, el navegador realiza de elementos no utilizados para detectar cuándo un objeto ya no es necesario y el bloque de memoria subyacente.

Sin embargo, la detección no es perfecta se demostró que la detección perfecta es una tarea imposible. Por lo tanto, los navegadores se aproximan a la noción de "un objeto si la solicitud no se encuentra aquí". con la noción de “un objeto es accesible”. Si la página web no puede alcance un objeto a través de sus variables y los campos de otros objetos accesibles, el navegador podrá reclamar el objeto de forma segura. La diferencia entre estos dos nociones conduce a fugas de memoria, como se ilustra en el siguiente ejemplo.

const object = {a: new Array(1000), b: new Array(2000)};
setInterval(() => console.log(object.a), 1000);

Aquí ya no se necesita el array más grande b, pero el navegador no lo necesita. y recupéralo, ya que aún se puede acceder a él mediante object.b en la devolución de llamada. Por lo tanto, se pierde la memoria del array más grande.

Las fugas de memoria son prevalentes en la Web. Es fácil introducir uno si olvidas cancelar el registro de un objeto de escucha de eventos, capturando objetos accidentalmente de un iframe, al no cerrar un trabajador, la acumulación de objetos en arrays, etcétera. Si una página web tiene fugas de memoria, el uso de memoria aumenta con el tiempo y la página web parece lenta inflado a los usuarios.

El primer paso para resolver este problema es medirlo. La nueva herramienta La API de performance.measureUserAgentSpecificMemory() permite que los desarrolladores que miden el uso de memoria de sus páginas web en producción y, por lo tanto, detectan la memoria que pasan por las pruebas locales.

¿En qué se diferencia performance.measureUserAgentSpecificMemory() de la API de performance.memory heredada?

Si conoces la API no estándar de performance.memory existente, Quizá te preguntes en qué se diferencia la nueva API. La principal diferencia es que la API anterior devuelve el tamaño del montón de JavaScript, mientras que la API nueva calcula la memoria usada por la página web. Esta diferencia se convierte importante cuando Chrome comparte el mismo montón con varias páginas web (o varias instancias de la misma página web). En tales casos, el resultado de la configuración La API puede estar desactivada de forma arbitraria. Dado que la API anterior se define en términos específicos de la implementación, como "montón", y estandarizarlo no tiene sentido.

Otra diferencia es que la nueva API realiza la medición de la memoria durante la recolección de elementos no utilizados. Esto reduce el ruido en los resultados, pero puede tardar un hasta que se producen los resultados. Ten en cuenta que otros navegadores pueden decidir implementar la nueva API sin depender de la recolección de elementos no utilizados.

Casos de uso sugeridos

El uso de memoria de una página web depende del tiempo de los eventos, de las acciones del usuario y recolecciones de elementos no utilizados. Por eso, la API de medición de memoria está diseñada para agregar datos de uso de memoria desde la producción. Los resultados de llamadas individuales son menos útiles. Casos prácticos de ejemplo:

  • Detección de regresión durante el lanzamiento de una versión nueva de la página web para detectar nuevas fugas de memoria.
  • Realiza pruebas A/B de una función nueva para evaluar su impacto en la memoria y detectar fugas de memoria.
  • Correlaciona el uso de memoria con la duración de la sesión para verificar la presencia o ausencia de fugas de memoria.
  • Correlaciona el uso de memoria con las métricas del usuario para comprender el impacto general del uso de memoria.

Compatibilidad del navegador

Navegadores compatibles

  • Chrome: 89.
  • Borde: 89.
  • Firefox: No es compatible.
  • Safari: no es compatible.

Origen

Actualmente, la API solo es compatible con los navegadores basados en Chromium a partir de Chrome 89. El resultado de la API depende en gran medida de la implementación porque los navegadores tienen las diferentes formas de representar objetos en la memoria y las diferentes formas la estimación del uso de memoria. Los navegadores pueden excluir algunas regiones de la memoria de contabilizar si una contabilidad adecuada es demasiado costosa o inviable. Por lo tanto, los resultados no se pueden comparar entre navegadores. Solo tiene sentido comparar resultados en el mismo navegador.

Usa performance.measureUserAgentSpecificMemory()

Detección de funciones

La función performance.measureUserAgentSpecificMemory no estará disponible o es posible que fallan con un SecurityError si el entorno de ejecución no cumple los requisitos de seguridad para prevenir filtraciones de información de origen cruzado. Se basa en el aislamiento de origen cruzado, que una página web puede activar. estableciendo encabezados COOP+COEP.

La compatibilidad se puede detectar en el entorno de ejecución:

if (!window.crossOriginIsolated) {
  console.log('performance.measureUserAgentSpecificMemory() is only available in cross-origin-isolated pages');
} else if (!performance.measureUserAgentSpecificMemory) {
  console.log('performance.measureUserAgentSpecificMemory() is not available in this browser');
} else {
  let result;
  try {
    result = await performance.measureUserAgentSpecificMemory();
  } catch (error) {
    if (error instanceof DOMException && error.name === 'SecurityError') {
      console.log('The context is not secure.');
    } else {
      throw error;
    }
  }
  console.log(result);
}

Pruebas locales

Chrome realiza la medición de la memoria durante la recolección de elementos no utilizados, lo que significa que la API no resuelva inmediatamente la promesa de resultado y espere para la próxima recolección de elementos no utilizados.

Llamar a la API fuerza la recolección de elementos no utilizados después de un tiempo de espera, que se que actualmente está establecido en 20 segundos, aunque puede suceder antes. Iniciar Chrome con la La marca de línea de comandos de --enable-blink-features='ForceEagerMeasureMemory' reduce el tiempo de espera a cero y es útil para pruebas y depuración locales.

Ejemplo

El uso recomendado de la API es definir un monitor de memoria global que muestra el uso de la memoria de toda la página web y envía los resultados a un servidor. para la agregación y el análisis. La forma más sencilla es muestrear de forma periódica, por ejemplo cada M minutos. Sin embargo, eso introduce sesgos en los datos porque pueden ocurrir picos de memoria entre las muestras.

En el siguiente ejemplo, se muestra cómo hacer mediciones de memoria imparciales con un proceso Poisson, que garantiza que las muestras tengan la misma probabilidad de ocurrir en cualquier momento (demostración, fuente).

Primero, define una función que programe la siguiente medición de memoria usando setTimeout() con un intervalo aleatorizado

function scheduleMeasurement() {
  // Check measurement API is available.
  if (!window.crossOriginIsolated) {
    console.log('performance.measureUserAgentSpecificMemory() is only available in cross-origin-isolated pages');
    console.log('See https://web.dev/coop-coep/ to learn more')
    return;
  }
  if (!performance.measureUserAgentSpecificMemory) {
    console.log('performance.measureUserAgentSpecificMemory() is not available in this browser');
    return;
  }
  const interval = measurementInterval();
  console.log(`Running next memory measurement in ${Math.round(interval / 1000)} seconds`);
  setTimeout(performMeasurement, interval);
}

La función measurementInterval() calcula un intervalo aleatorio en milisegundos. para que, en promedio, haya una medición cada cinco minutos. Consulta Exponencial distribución si te interesan los cálculos detrás de la función.

function measurementInterval() {
  const MEAN_INTERVAL_IN_MS = 5 * 60 * 1000;
  return -Math.log(Math.random()) * MEAN_INTERVAL_IN_MS;
}

Por último, la función performMeasurement() asíncrona invoca la API, los registros el resultado y programa la siguiente medición.

async function performMeasurement() {
  // 1. Invoke performance.measureUserAgentSpecificMemory().
  let result;
  try {
    result = await performance.measureUserAgentSpecificMemory();
  } catch (error) {
    if (error instanceof DOMException && error.name === 'SecurityError') {
      console.log('The context is not secure.');
      return;
    }
    // Rethrow other errors.
    throw error;
  }
  // 2. Record the result.
  console.log('Memory usage:', result);
  // 3. Schedule the next measurement.
  scheduleMeasurement();
}

Por último, empieza a medir.

// Start measurements.
scheduleMeasurement();

El resultado puede verse de la siguiente manera:

// Console output:
{
  bytes: 60_100_000,
  breakdown: [
    {
      bytes: 40_000_000,
      attribution: [{
        url: 'https://example.com/',
        scope: 'Window',
      }],
      types: ['JavaScript']
    },

    {
      bytes: 20_000_000,
      attribution: [{
          url: 'https://example.com/iframe',
          container: {
            id: 'iframe-id-attribute',
            src: '/iframe',
          },
          scope: 'Window',
      }],
      types: ['JavaScript']
    },

    {
      bytes: 100_000,
      attribution: [],
      types: ['DOM']
    },
  ],
}

La estimación del uso total de memoria se muestra en el campo bytes. Este valor es de depende en gran medida de la implementación y no se puede comparar entre navegadores. Puede incluso cambiar entre diferentes versiones del mismo navegador. El valor incluye Memoria JavaScript y DOM de todos los iframes, las ventanas relacionadas y los trabajadores web de el proceso actual.

En la lista breakdown, se proporciona más información sobre la memoria usada. Cada describe una parte de la memoria y la atribuye a un conjunto de ventanas, iframes y trabajadores identificados por URL. El campo types enumera los tipos de memoria específicos de la implementación asociados con la memoria.

Es importante tratar todas las listas de forma genérica y no codificar en función de un navegador en particular. Por ejemplo, es posible que algunos navegadores Se muestra un breakdown vacío o un attribution vacío. Es posible que otros navegadores mostrar varias entradas en attribution que indican que no pudieron distinguir cuál de estas entradas posee la memoria.

Comentarios

Al grupo de la comunidad de rendimiento web y al equipo de Chrome les encantaría conocer tus opiniones y experiencias con performance.measureUserAgentSpecificMemory()

Cuéntanos sobre el diseño de la API

¿Existe algún aspecto de la API que no funcione según lo esperado? ¿O hay propiedades faltantes para implementar tu idea? Informa un problema de especificaciones sobre performance.measureUserAgentSpecificMemory() GitHub o agrega tus ideas a un problema existente.

Informar un problema con la implementación

¿Encontraste un error en la implementación de Chrome? ¿O la implementación diferente de la especificación? Informa un error en new.crbug.com. Asegúrate de incluye todos los detalles que puedas, brinda instrucciones sencillas para reproducir el error y establecer los componentes en Blink>PerformanceAPIs. Glitch funciona muy bien para compartir repros rápidos y fáciles.

Demostrar apoyo

¿Planeas usar performance.measureUserAgentSpecificMemory()? Tu apoyo público ayuda al equipo de Chrome a priorizar funciones y muestra a otros proveedores de navegadores cómo hacerlo esencial para respaldarlos. Envía un tweet a @ChromiumDev. y cuéntanos dónde y cómo la utilizas.

Vínculos útiles

Agradecimientos

Muchas gracias a Domenic Denicola, Yoav Weiss y Mathias Bynens por las revisiones de diseño de APIs y Dominik Inführ, Hannes Payer, Kentaro Hara y Michael Lippautz para las revisiones de código en Chrome. También agradezco a Per Parker, Philipp Weis, Olga Belomestnykh, Bolohan y Neil Mckay por proporcionar comentarios valiosos de los usuarios que mejoró la API.

Hero image de Harrison Broadbent en Unsplash