Dev

Supervise el uso total de memoria de su página web con meterMemory ()

Aprenda a medir el uso de memoria de su página web en producción para detectar regresiones.

Los navegadores gestionan la memoria de las páginas web de forma automática. Siempre que una página web crea un objeto, el navegador asigna una parte de la memoria “debajo del capó” para almacenar el objeto. Dado que la memoria es un recurso finito, el navegador realiza una recolección de basura para detectar cuando un objeto ya no es necesario y para liberar el bloque de memoria subyacente. Sin embargo, la detección no es perfecta y
fue probado que la detección perfecta es una tarea imposible. Por lo tanto, los navegadores aproximan la noción de “un objeto es necesario” con la noción de “un objeto es accesible”. Si la página web no puede llegar a un objeto a través de sus variables y los campos de otros objetos accesibles, entonces el navegador puede recuperar el objeto de forma segura. La diferencia entre estas dos nociones conduce a pérdidas 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í la matriz más grande b ya no es necesario, pero el navegador no lo recupera porque todavía es accesible a través de object.b en la devolución de llamada. Por lo tanto, se filtra la memoria de la matriz más grande.

Las fugas de memoria son prevalente en la Web. Es fácil introducir uno olvidándose de anular el registro de un detector de eventos, capturando accidentalmente objetos de un iframe, no cerrando un trabajador, acumulando objetos en matrices, etc. Si una página web tiene pérdidas de memoria, su uso de memoria aumenta con el tiempo y la página web parece lenta e hinchada para los usuarios.

El primer paso para resolver este problema es medirlo. El nuevo
performance.measureMemory() API permite a los desarrolladores medir el uso de memoria de sus páginas web en producción y así detectar pérdidas de memoria que se escapan a través de pruebas locales.

Como es performance.measureMemory() diferente al legado performance.memory API?

Si está familiarizado con el no estándar existente performance.memory API, es posible que se pregunte 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 nueva API estima el uso de memoria de toda la página web. Esta diferencia se vuelve 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 antigua API puede estar arbitrariamente apagado. Dado que la antigua API se define en términos específicos de implementación como “montón”, estandarizarla es inútil.

Otra diferencia es que la nueva API realiza mediciones de memoria durante la recolección de basura. Esto reduce el ruido en los resultados, pero puede llevar un tiempo hasta que se produzcan los resultados. Tenga en cuenta que otros navegadores pueden decidir implementar la nueva API sin depender de la recolección de basura.

Casos de uso sugeridos

El uso de memoria de una página web depende del momento de los eventos, las acciones del usuario y las recolecciones de basura. Es por eso que la API de medición de memoria está diseñada para agregar datos de uso de memoria de producción. Los resultados de las llamadas individuales son menos útiles. Casos de uso de ejemplo:

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

Compatibilidad del navegador

Actualmente, la API solo es compatible con Chrome 83 como prueba de origen. El resultado de la API depende en gran medida de la implementación porque los navegadores tienen diferentes formas de representar objetos en la memoria y diferentes formas de estimar el uso de la memoria. Los navegadores pueden excluir algunas regiones de memoria de la contabilidad si la contabilidad adecuada es demasiado cara o inviable. Por lo tanto, los resultados no se pueden comparar entre navegadores. Solo tiene sentido comparar los resultados para el mismo navegador.

Estado actual

Paso Estado
1. Crea un explicador Completar
2. Crear borrador inicial de especificación En progreso
3. Recopile comentarios y repita el diseño En progreso
4. Prueba de origen En progreso
5. Lanzamiento No empezado

Utilizando performance.measureMemory()

Habilitar el apoyo durante la fase de prueba de origen

los performance.measureMemory() La API está disponible como prueba de origen a partir de Chrome 83. Se espera que la prueba de origen finalice en Chrome 86 a principios de noviembre de 2020.

Las pruebas de Origin le permiten probar nuevas funciones y brindar comentarios sobre su usabilidad, practicidad y efectividad a la comunidad de estándares web. Para obtener más información, consulte el Guía de pruebas de Origin para desarrolladores web. Para inscribirse en esta u otra prueba de origen, visite el página de registro.

Regístrese para la prueba de origen

  1. Solicita un token por tu origen.
  2. Agrega el token a tus páginas. Hay dos maneras de hacerlo:
    • Agregar un origin-trial <meta> etiqueta al encabezado de cada página. Por ejemplo, esto puede verse así:
      <meta http-equiv="origin-trial" content="TOKEN_GOES_HERE">
    • Si puede configurar su servidor, también puede agregar el token usando un Origin-Trial Encabezado HTTP. El encabezado de respuesta resultante debería verse así:
      Origin-Trial: TOKEN_GOES_HERE

Habilitación a través de chrome: // flags

Para experimentar con performance.measureMemory() sin un token de prueba de origen, habilite el #experimental-web-platform-features bandera en chrome://flags.

Detección de características

los performance.measureMemory() la función puede fallar con un
Error de seguridad si el entorno de ejecución no cumple con los requisitos de seguridad para prevenir fugas de información de origen cruzado. Durante la prueba de origen en Chrome, la API requiere que Aislamiento del sitio está habilitado. Cuando se envíe la API, dependerá de
aislamiento de origen cruzado. Una página web puede optar por el aislamiento de origen cruzado configurando Encabezados COOP + COEP.

Quizás te interesa >>>  Cinco formas en que AirSHIFT mejoró el rendimiento en tiempo de ejecución de su aplicación React

if (performance.measureMemory) {
let result;
try {
result = await performance.measureMemory();
} 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 basura. Esto significa que la API no resuelve la promesa de resultado de inmediato y, en cambio, espera la siguiente recolección de basura. La API fuerza una recolección de basura después de un tiempo de espera, que actualmente está establecido en 20 segundos. Iniciando Chrome con el
--enable-blink-features='ForceEagerMeasureMemory' El indicador de línea de comandos reduce el tiempo de espera a cero y es útil para la depuración y las pruebas locales.

Ejemplo

El uso recomendado de la API es definir un monitor de memoria global que muestree el uso de memoria de toda la página web y envíe los resultados a un servidor para su agregación y análisis. La forma más sencilla es tomar muestras periódicamente, por ejemplo, cada M minutos. Sin embargo, esto introduce un sesgo en los datos porque pueden producirse picos de memoria entre las muestras. El siguiente ejemplo muestra cómo realizar mediciones de memoria insesgadas utilizando un Proceso de Poisson, lo que garantiza que las muestras tengan la misma probabilidad de ocurrir en cualquier momento (manifestación, fuente).

Primero, defina una función que programe la siguiente medición de memoria usando
setTimeout() con un intervalo aleatorio. La función debe llamarse después de cargar la página en la ventana principal.

function scheduleMeasurement() {
if (!performance.measureMemory) {
console.log("performance.measureMemory() is not available.");
return;
}
const interval = measurementInterval();
console.log("Scheduling memory measurement in " +
Math.round(interval / 1000) + " seconds.");
setTimeout(performMeasurement, interval);
}


window.onload = function () {
scheduleMeasurement();
}

los measurementInterval() La función calcula un intervalo aleatorio en milisegundos de modo que, en promedio, hay una medición cada cinco minutos. Ver Distribución exponencial si está interesado en las matemáticas 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;
}

Finalmente, el async performMeasurement() La función invoca la API, registra el resultado y programa la siguiente medición.

async function performMeasurement() {
let result;
try {
result = await performance.measureMemory();
} catch (error) {
if (error instanceof DOMException &&
error.name === "SecurityError") {
console.log("The context is not secure.");
return;
}
throw error;
}
console.log("Memory usage:", result);
scheduleMeasurement();
}

El resultado puede verse como sigue:


{
bytes: 60_000_000,
breakdown: [
{
bytes: 40_000_000,
attribution: ["https://foo.com"],
userAgentSpecificTypes: ["Window", "JS"]
},
{
bytes: 20_000_000,
attribution: ["https://foo.com/iframe"],
userAgentSpecificTypes: ["Window", "JS"]
}
]
}

La estimación del uso total de memoria se devuelve en el bytes campo. El valor de bytes está usando sintaxis del separador numérico. Este valor depende en gran medida de la implementación y no se puede comparar entre navegadores. Incluso puede cambiar entre diferentes versiones del mismo navegador. Durante la prueba de origen, el valor incluye el uso de memoria JavaScript de la ventana principal y todos
mismo sitio iframes y ventanas relacionadas. Cuando se envía la API, el valor tendrá en cuenta JavaScript y la memoria DOM de todos los iframes, ventanas relacionadas y trabajadores web.

los breakdown La lista proporciona más información sobre la memoria utilizada. Cada entrada describe una parte de la memoria y la atribuye a un conjunto de ventanas, iframes y trabajadores identificados por URL. los userAgentSpecificTypes
El campo 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 suposiciones basadas en un navegador en particular. Por ejemplo, algunos navegadores pueden devolver un breakdown o un vacio attribution. Otros navegadores pueden devolver varias URL en attribution lo que indica que no pudieron distinguir cuál de estas URL es propietaria de la memoria.

Realimentación

los Grupo comunitario de rendimiento web y al equipo de Chrome le encantaría conocer sus opiniones y experiencias con
performance.measureMemory().

Cuéntanos sobre el diseño de la API

¿Hay algo en la API que no funcione como se esperaba? ¿O faltan propiedades que necesitas para implementar tu idea? Presentar un problema de especificaciones en el performance.measureMemory Repositorio de GitHub o agregue sus pensamientos a un problema existente.

Informar un problema con la implementación

¿Encontraste un error con la implementación de Chrome? ¿O la implementación es diferente de la especificación? Presentar un error en new.crbug.com. Asegúrese de incluir todos los detalles que pueda, proporcione instrucciones sencillas para reproducir el error y Componentes ajustado a Blink>PerformanceAPIs.
Falla funciona muy bien para compartir repros rápidos y fáciles.

Mostrar apoyo

¿Está planeando usar performance.measureMemory()? Su apoyo público ayuda al equipo de Chrome a priorizar funciones y muestra a otros proveedores de navegadores lo importante que es brindarles soporte. Enviar un tweet a @Cromodev y háganos saber dónde y cómo lo está usando.

Enlaces Útiles

Agradecimientos

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

Imagen de héroe por Harrison Broadbent en Unsplash