garbage.collect ()

Para ejecutar JavaScript, el navegador necesita un poco de memoria, pero en algún lugar necesita almacenar objetos, primitivas, funciones que se crean para todas las acciones del usuario. Por lo tanto, el navegador primero asigna la cantidad requerida de RAM, y cuando no se usan objetos, lo limpia de forma independiente.

En teoría, suena bien. En la práctica, el usuario abre 20 pestañas de YouTube, redes sociales, lee algo, funciona, el navegador consume memoria, como Hummer H2 - gasolina. El recolector de basura, como este monstruo con un trapeador, recorre toda la memoria y agrega confusión, todo se ralentiza y se bloquea.



Para evitar que tales situaciones sucedan y que el rendimiento de nuestros sitios y aplicaciones no sufra, el desarrollador front-end debe saber cómo la basura afecta a las aplicaciones, cómo el navegador las recoge y optimiza el manejo de la memoria, y cómo todo difiere de la dura realidad. Este es solo el informe de Andrei Roenko ( flapenguin ) en Frontend Conf 2018 .

Usamos el recolector de basura (no en casa, en el desarrollo front-end) todos los días, pero realmente no pensamos en lo que es, lo que nos cuesta y las oportunidades y limitaciones que tiene.

Si la recolección de basura realmente funcionara en JavaScript, la mayoría de los módulos npm se eliminarían inmediatamente después de la instalación.

Pero aunque esto no sea así, hablaremos de lo que es, de ensamblar objetos innecesarios.


Sobre el orador : Andrei Roenko ha estado desarrollando Yandex.Map API , ha estado en la interfaz durante seis años, le encanta crear sus propias altas abstracciones y descender de otras personas al suelo.

¿Por qué necesitas recolección de basura?


Considere el ejemplo de Yandex.Maps. Yandex.Maps es un servicio enorme y complejo que utiliza muchos JS y casi todas las API de navegador existentes, excepto las multimedia, y el tiempo de sesión promedio es de 5-10 minutos. La abundancia de JavaScript crea muchos objetos. Arrastrar el mapa, agregar organizaciones, resultados de búsqueda y muchos otros eventos que ocurren cada segundo crea una avalancha de objetos. Agregue a esto Reaccionar y los objetos se vuelven aún más.

Sin embargo, los objetos JS ocupan solo 30–40 Mb en el mapa. Para largas sesiones de Yandex.Maps y la asignación constante de nuevos objetos, esto no es suficiente.

La razón del pequeño volumen de objetos es que el recolector de basura los recopila con éxito y la memoria se reutiliza.

Hoy hablaremos sobre la recolección de basura desde cuatro lados:

  • Teoría Comencemos con ella para hablar el mismo idioma y entenderse.
  • La dura realidad. En última instancia, la computadora ejecuta código de máquina en el que no hay todas las abstracciones que nos son familiares. Tratemos de descubrir cómo funciona la recolección de basura en un nivel bajo.
  • Navegador de la realidad. Veamos cómo se implementa la recolección de basura en los motores y navegadores modernos, y qué conclusiones podemos sacar de esto.
  • Vida cotidiana : hablemos de la aplicación práctica del conocimiento adquirido en la vida cotidiana.

Todas las declaraciones están respaldadas con ejemplos de cómo y cómo no hacerlo.

¿Por qué saber todo esto?


La recolección de basura es algo invisible para nosotros, sin embargo, sabiendo cómo se organiza, usted:

  • Tenga una idea de la herramienta que está utilizando, que es útil en su trabajo.
  • Comprenda dónde optimizar las aplicaciones ya lanzadas y cómo diseñar las futuras para que funcionen mejor y más rápido.
  • Sepa cómo no cometer errores comunes y dejar de malgastar recursos en "optimizaciones" inútiles y dañinas.

Teoría


Joel Spolsky dijo una vez:

Todas las abstracciones no triviales tienen fugas.

El recolector de basura es una gran abstracción no trivial que se parchea desde todos los lados. Afortunadamente, fluye muy raramente.

Comencemos con una teoría, pero sin definiciones aburridas. Analicemos el trabajo del recopilador usando un código simple como ejemplo:

window.Foo = class Foo { constructor() { this.x = { y: 'y' }; } work(name) { let z = 'z'; return function () { console.log(name, this.xy, z); this.x = null; }.bind(this); } }; 

  • Hay una clase en el código.
  • La clase tiene un constructor .
  • El método de trabajo devuelve una función relacionada.
  • Dentro de la función, se utilizan esta y un par de variables del cierre.

Veamos cómo se comportará este código si lo ejecutamos de esta manera:

 var foo = new Foo(); //C   window.worker = foo.work('Brendan Eich'); //     bind,   window.foo = null; //   window.Foo = null; //  ,   -  window.worker(); window.worker = null; //   ,   

Analicemos el código y sus componentes con más detalle y comencemos con la clase.

Declaración de clase




Podemos suponer que las clases en ECMAScript 2015 son solo azúcar sintáctica para funciones. Todas las funciones tienen:

  • Función. [[Prototipo]] es el prototipo real de la función.
  • Foo.prototype es un prototipo para objetos recién creados.
  • Foo.prototype tiene un enlace de regreso al constructor a través del campo del constructor. Este es un objeto, por lo que hereda de Object.prototype .
  • El método de trabajo es una función separada para la que hay un enlace, similar al constructor, porque ambas son solo funciones. También puede establecer un prototipo y llamarlo a través de nuevo, pero rara vez alguien usa este comportamiento.

Los prototipos ocupan mucho espacio en el circuito, así que recordemos que lo son, pero los eliminarán por simplicidad.

Crear un objeto de clase




  • Ponemos nuestra clase en la ventana, porque las clases no llegan allí por defecto.
  • Crea un objeto de clase.
  • La creación de un objeto expone automáticamente el prototipo del objeto de clase en Foo.prototype. Por lo tanto, cuando intente llamar al método de trabajo en un objeto, sabrá qué tipo de trabajo es.
  • Nuestro constructor crea el campo x en el objeto a partir del objeto con la cadena.

Esto es lo que sucedió:



El método devuelve una función enlazada: este es un objeto "mágico" tan especial en JS, que consiste en un enlace esto y una función que debe llamarse. La función relacionada también tiene un prototipo y otro prototipo, pero estamos interesados ​​en el cierre. Por especificación, el cierre se almacena en el entorno. Lo más probable es que esté más familiarizado con la palabra Ámbito, pero en las especificaciones el campo se llama Medio ambiente .



The Environment almacena una referencia a LexicalEnvironment. Este es un objeto complejo, más complicado que en una diapositiva; almacena enlaces a todo lo que se puede acceder desde una función. Por ejemplo, ventana, Foo, nombre y z. También almacena enlaces incluso a lo que no está utilizando explícitamente. Por ejemplo, puede usar eval y accidentalmente usar objetos no utilizados, pero JS no debe romperse.

Entonces, construimos todos los objetos y ahora destruiremos todo.

Eliminar el enlace al objeto


Comencemos eliminando el enlace al objeto, este enlace en el diagrama está resaltado en rojo.



Eliminamos y no sucede nada, porque desde la ventana hasta el objeto hay una ruta a través de la función de función enlazada .



Esto nos empuja a un error típico.

Error común: suscripción olvidada


 externalElement.addEventListener('click', () => { if (this.shouldDoSomethingOnClick) { this.doSomething(); } }) 

Ocurre cuando se suscribe: usando esto, explícitamente a través de las funciones de enlace o flecha; usa algo en el cierre. Luego olvida darse de baja, y la vida útil de su objeto o lo que está en el circuito es igual a la vida útil de una suscripción. Por ejemplo, si este es un elemento DOM que no toca, entonces es muy probable que sea el tiempo hasta el final de la vida de la página.

Para resolver estos problemas:

  • Darse de baja
  • Piense en la duración de la suscripción y en quién es el propietario.
  • Si por alguna razón no puede darse de baja, anule los enlaces (lo que sea = nulo) o limpie todos los campos del objeto. Si su objeto tiene fugas, será pequeño y no es una pena.
  • Use WeakMap, tal vez esto ayude en algunas situaciones.

Eliminar la referencia de clase


Siga adelante e intente eliminar el enlace rojo resaltado en la clase.



Eliminamos el enlace y nada cambia para nosotros. La razón es que la clase es accesible a través de BoundThis, en el que hay un enlace al prototipo, y en el prototipo hay un enlace de regreso al constructor.

Error típico de trabajo inútil


¿Por qué se necesitan todas estas demostraciones? Porque hay una otra cara del problema cuando la gente toma el consejo de anular los enlaces demasiado literalmente y anular todo en general.

 destroy() { this._x = null; this._y = null; //  10 this._foobar = null } 

Este es un trabajo bastante inútil. Si el objeto consta solo de referencias a otros objetos y no hay recursos allí, entonces no se necesita destroy (). Es suficiente perder la referencia al objeto, y él morirá solo.

No hay un consejo universal. Cuando sea necesario, anule, y cuando no, no anule. La reducción a cero no es un error, sino simplemente un trabajo inútil.

Adelante Llame al método de función enlazada y eliminará el enlace de [objeto Foo] a [objeto Objeto]. Esto conducirá al hecho de que los objetos que se encuentran separados en un rectángulo azul aparecen en el diagrama.



Estos objetos son basura JS. El va genial. Sin embargo, hay basura que no se puede recoger.

Basura que no va a


En muchas API de navegador, puede crear y destruir un objeto. Si el objeto no se destruye, entonces ningún recolector puede ensamblarlo.

Objetos con funciones de creación / eliminación de pares:

  • createObjectURL (), revokeObjectURL ();
  • WebGL: crear / eliminar Programa / Shader / Buffer / Texture / etc;
  • ImageBitmap.close ();
  • indexDb.close ().

Por ejemplo, si olvida eliminar ObjectURL de un video de 200 MB, entonces en la memoria estos 200 MB permanecerán hasta el final de la vida de la página e incluso más, porque hay intercambio de datos entre las pestañas. Del mismo modo en WebGL, indexDb y otras API de navegador con recursos similares.

Afortunadamente, en nuestro ejemplo, el rectángulo azul contiene solo objetos JavaScript, por lo que esto es solo basura que se puede eliminar.

El siguiente paso es borrar el último enlace de izquierda a derecha. Esta es una referencia al método que recibimos, una función relacionada.



Después de su eliminación, ¿no tendremos enlaces a izquierda y derecha? De hecho, todavía hay enlaces desde el cierre.



Es importante que no haya enlaces de izquierda a derecha, por lo tanto, todo excepto la ventana es basura y morirá.

Nota importante : hay referencias circulares en la basura, es decir, objetos que se refieren entre sí. La presencia de dichos enlaces no afecta nada, porque el recolector de basura no recolecta objetos individuales, sino toda la basura.



Observamos los ejemplos y ahora, en un nivel intuitivo, entendemos qué es la basura, pero demos una definición completa del concepto.

La basura es todo lo que no es un objeto vivo.

Todo se hizo muy claro. Pero, ¿qué es un objeto vivo?

Un objeto vivo es un objeto al que se puede llegar mediante enlaces desde el objeto raíz.

Aparecen dos conceptos nuevos: "seguir enlaces" y "objeto raíz". Un objeto raíz que ya conocemos es window, así que comencemos con los enlaces.

¿Qué significa seguir los enlaces?


Hay muchos objetos que están relacionados entre sí y se refieren entre sí. Agitaremos a lo largo de ellos, comenzando con el objeto raíz.

Inicializamos el primer paso, y luego procedemos de acuerdo con el siguiente algoritmo: digamos que todo en la cresta de la ola son objetos vivos y vemos a qué se refieren.



Inicializamos el primer paso. Luego, actuaremos de acuerdo con el siguiente algoritmo: digamos que todo lo amarillo en la cresta de la ola son objetos vivos y veamos a qué se refieren.

A lo que se refieren, haremos una nueva cresta de la ola:



Terminado y comenzar de nuevo:

  • Estamos reviviendo
  • Vemos a qué se refieren.
  • Crea una nueva cresta de ola, anima objetos.
  • Vemos a qué se refieren.



Al notar que una flecha apunta a un objeto ya vivo, simplemente no hacemos nada. Además, según el algoritmo, hasta que se agoten los objetos a recorrer. Luego decimos que encontramos todos los objetos vivos, y todo lo demás es basura.



Este proceso se llama marcado .

¿Qué significa el objeto raíz?



  • Ventana
  • Casi todas las API del navegador.
  • Todos lo prometen.
  • Todo lo que se pone en Microtask y Macrotask.
  • Observadores de mutaciones, RAF, devoluciones de llamadas inactivas. Todo lo que se puede alcanzar de lo que se encuentra en la RAF no se puede eliminar, porque si elimina el objeto que se utiliza en la RAF, entonces probablemente algo salga mal.

El ensamblaje puede ocurrir en cualquier momento. Cada vez que aparecen llaves o funciones, se crea un nuevo objeto. Puede que no haya suficiente memoria, y el recolector irá a buscar gratis:

 function foo (a, b, c) { function bar (x, y, z) { const x = {}; // nomem, run gc D: // … } while (whatever()) bar(); } 

En este caso, los objetos raíz serán todo en la pila de llamadas. Si, por ejemplo, se detiene en la línea con X y elimina a lo que se refiere Y, entonces su aplicación se bloqueará. JS no nos permite tales frivolidades, por lo que no puede eliminar un objeto de Y.

Si la parte anterior parecía complicada, entonces será aún más difícil.

Dura realidad


Hablemos del mundo de las máquinas en el que tratamos con hierro, con medios físicos.

La memoria es una gran matriz en la que solo se encuentran los números, por ejemplo: nueva Uint32Array (16 * 2 ** 30).

Vamos a crear objetos en la memoria y agregarlos de izquierda a derecha. Creamos uno, segundo, tercero, todos son de diferentes tamaños. Ponemos enlaces en el camino.


En el séptimo objeto, el lugar ha terminado, porque tenemos 2 casillas libres, pero necesitamos 5.

¿Qué se puede hacer aquí? La primera opción es bloquearse. En el patio en 2018, todos tienen las últimas MacBooks y 16 GB de RAM. ¡No hay situaciones en las que no hay memoria!

Sin embargo, dejar que las cosas sigan su curso es una mala idea, porque en la web esto lleva a una pantalla similar:



Este no es el comportamiento que queremos del programa, pero en general es válido. Hay una categoría de coleccionistas llamada No-op .

Coleccionista sin operaciones


Pros:

  • El coleccionista es muy simple.
  • Simplemente no hay recolección de basura.
  • No es necesario escribir ni pensar en la memoria.

Contras:

  • Todo cae para que nunca vuelva a subir.

Para el frontend, el recopilador no operativo es irrelevante, pero se usa en el backend. Por ejemplo, al tener varios servidores detrás de los equilibradores, la aplicación recibe 32 GB de RAM y luego se elimina por completo. Es más simple y el rendimiento solo se mejora simplemente reiniciando cuando la memoria se vuelve baja.

En la web es imposible y hay que limpiarlo.

Buscar y eliminar basura


Comenzamos a limpiar con basura. Ya sabemos cómo hacerlo. Basura: objetos C y F en el esquema anterior, porque no puede alcanzarlos a lo largo de las flechas desde el objeto raíz.

Tomamos esta basura, se la damos al amante de la basura y ya está.



Después de la limpieza, el problema no se resuelve, ya que los agujeros permanecen en la memoria. Tenga en cuenta que hay 7 casillas libres, pero 5 de ellas aún no podemos asignarlas. La fragmentación ocurrió y la asamblea terminó. Tal algoritmo con agujeros se llama Mark y Sweep .

Marcar y barrer


Pros:

  • Un algoritmo muy simple. Uno de los primeros que aprenderá si comienza a aprender sobre el recolector de basura.
  • Funciona en proporción a la cantidad de basura, pero solo hace frente cuando hay poca basura.
  • Si solo tienes objetos vivos, entonces él no pierde el tiempo y simplemente no hace nada.

Contras:

  • Se requiere una lógica compleja para buscar espacio libre, porque cuando hay muchos agujeros en la memoria, debe probar un objeto en cada uno para comprender si encaja o no.
  • Fragmentos de memoria. Puede ocurrir una situación en la que con 200 MB libres la memoria se divide en partes pequeñas y, como en el ejemplo anterior, no hay una parte sólida de memoria para el objeto.

Estamos buscando otras ideas. Si miras la imagen y piensas, lo primero es cambiar todo a la izquierda. Luego, a la derecha, habrá una pieza grande y libre, en la que nuestro objeto se ajustará con calma.

Existe dicho algoritmo y se llama Mark y Compact .

Marca y compacto


Pros:

  • Desfragmentar la memoria.
  • Funciona en proporción al número de objetos vivos, lo que significa que puede usarse cuando prácticamente no hay basura.

Contras:

  • Difícil en el trabajo y la implementación.
  • Mueve objetos. Movimos el objeto, lo copiamos, ahora está en un lugar diferente y toda la operación es bastante costosa.
  • Requiere 2-3 pases en la memoria, dependiendo de la implementación: el algoritmo es lento.

Aquí llegamos a otra idea.

La recolección de basura no es gratuita.


En las API de alto rendimiento, como WebGL, WebAudio y WebGPU, que todavía está en desarrollo, los objetos se crean y eliminan en fases separadas. Estas especificaciones están escritas para que la recolección de basura no esté en proceso. Además, ni siquiera hay Promesa allí, sino pull (): solo pregunta a cada cuadro: "¿Pasó algo o no?".

Semispace aka Lisp 2


Hay otro coleccionista del que quiero hablar. ¿Qué sucede si no libera memoria, sino que copia todos los objetos vivos en otro lugar?

Intentemos copiar el objeto raíz "tal cual", que se refiere a alguna parte.



Y luego todos los demás.



No hay escombros ni agujeros en la memoria de arriba. Todo parece estar bien, pero surgen dos problemas:

  • Objetos duplicados: tenemos dos objetos verdes y dos azules. ¿Cuál usar?
  • Los enlaces de objetos nuevos conducen a objetos antiguos y no entre sí.

Con los enlaces, todo se resuelve con la ayuda de una "magia" algorítmica especial, y podemos hacer frente a la duplicación de objetos eliminando todo lo siguiente.


Como resultado, tenemos espacio libre y solo objetos vivos en el orden normal anterior. Este algoritmo se llama Semispace , Lisp 2, o simplemente el "recopilador de copias".

Pros:

  • Desfragmentar la memoria.
  • Simple
  • Se puede combinar con una fase de derivación.
  • Funciona en proporción al número de objetos vivos a lo largo del tiempo.
  • Funciona bien cuando hay mucha basura. Si tiene 2 GB de memoria y 3 objetos, omitirá solo 3 objetos y los 2 GB restantes parecen haberse ido.

Contras:

  • Doble consumo de memoria. Utiliza la memoria 2 veces más de lo necesario.
  • Mover objetos tampoco es una operación muy barata.

Nota: los recolectores de basura pueden mover objetos.

En la web, esto es irrelevante, pero en Node.js incluso mucho. Si escribe la extensión en C ++, entonces el lenguaje no sabe todo esto, por lo que hay enlaces dobles llamados manejador y se ven así: v8 :: Local <v8 :: String>.

Por lo tanto, si va a escribir complementos para Node.js, la información será útil.

Resumimos los diferentes algoritmos con sus pros y contras en la tabla. También tiene un algoritmo Eden, pero sobre eso más tarde.



Realmente quiero un algoritmo sin contras, pero esto no es así. Por lo tanto, tomamos lo mejor de todos los mundos: utilizamos varios algoritmos al mismo tiempo. En una pieza de memoria, recolectamos basura con un algoritmo y en otra con otro algoritmo.

¿Cómo entender la efectividad del algoritmo en tal situación?

Podemos utilizar el conocimiento de los maridos inteligentes de los años 60 que observaron todos los programas y se dieron cuenta:

Hipótesis generacional débil: la mayoría de los objetos mueren jóvenes.

Estos querían decir que todos los programas solo hacen que produzcan basura. En un intento de usar el conocimiento, llegaremos a lo que se llama "ensamblaje por generaciones".

Asamblea generacional


Creamos dos memorias que no están conectadas de ninguna manera: a la izquierda está Eden, y a la derecha están Mark y Sweep lentos. En el Edén creamos objetos. Muchos objetos



Cuando Eden dice que está lleno, comenzamos a recolectar basura. Encontramos objetos vivos y los copiamos a otro coleccionista.



El propio Edén está completamente limpio, y podemos agregarle más objetos.



Basándonos en la hipótesis generacional, decidimos que los objetos c, g, i probablemente vivirán mucho tiempo, y podemos verificarlos con menos frecuencia. Conociendo esta hipótesis, puede escribir programas que engañen al coleccionista. Esto se puede hacer, pero no te aconsejo, porque casi siempre dará lugar a efectos no deseados. Si crea basura de larga duración, el recolector comenzará a creer que no es necesario recolectarla.

Un ejemplo clásico de trampa es LRU-cache. Un objeto permanece en el caché durante mucho tiempo, el recolector lo mira y cree que aún no lo recogerá, porque el objeto vivirá durante mucho tiempo. Luego, un nuevo objeto ingresa al caché, y uno grande y viejo es expulsado de él y ya no es posible ensamblar inmediatamente este gran objeto.

Cómo recoger ahora lo sabemos. Hable sobre cuándo recolectar.

Cuando coleccionar?


La opción más fácil es cuando simplemente detenemos todo , comenzamos la compilación y luego comenzamos a trabajar de nuevo en JS.



En las computadoras modernas, más de un hilo de ejecución. En la web, esto es familiar por parte de Web Workers. ¿Por qué no tomar y paralelizar el proceso de ensamblaje ? Realizar varias operaciones pequeñas al mismo tiempo será más rápido que uno grande.



Otra idea es hacer una instantánea del estado actual y construir en paralelo con JS .



Si esto le interesa, le aconsejo que lea:

  • El único y principal libro de montaje, Garbage Collection Handbook.
  • Wikipedia como un recurso universal.
  • Sitio web memorymanagement.org.
  • Informes y artículos de Alexander Shepelev . Habla de Java, pero en términos de basura, Java y V8 funcionan más o menos igual.

Realidad del navegador


Pasemos a cómo los navegadores usan todo lo que hemos hablado.

Motores de IoT


Comencemos no con los navegadores, sino con los motores de Internet de las cosas: JerryScript y Duktape. Utilizan los algoritmos Mark'n'sweep y Stop the world.

Los motores IoT funcionan en microcontroladores, lo que significa que el lenguaje es lento; segundo cuelga; fragmentación y todo esto para una tetera con iluminación :)

Si escribes Internet of Things en JavaScript, ¿cuéntanoslo en los comentarios? hay algun punto

Dejaremos los motores de IoT solos, estamos interesados ​​en:

  • V8.
  • SpiderMonkey De hecho, él no tiene un logotipo. Logotipo casero :)
  • JavaScriptCore utilizado por WebKit.
  • ChakraCore que se usa en Edge.



Todos los motores son aproximadamente iguales, por lo que hablaremos de V8, como el más famoso.

V8


  • Casi todo el JavaScript del lado del servidor, porque es Node.js.
  • Casi el 80% de JavaScript del lado del cliente.
  • Los desarrolladores más sociables, hay mucha información y buenos códigos fuente que son más fáciles de leer.

El V8 utiliza ensamblaje generacional.


La única diferencia es que solíamos tener dos coleccionistas, y ahora tres:

  • Se crea un objeto en el Edén.
  • En algún momento en el Edén, hay demasiada basura y el objeto se transfiere al Semispace.
  • El objeto es joven y cuando el recolector se da cuenta de que es demasiado viejo y aburrido, lo arroja a Mark and Sweep, en el que la recolección de basura es extremadamente rara.

Puede ver claramente cómo se ve en la traza de memoria .



Se notan varias olas grandes con olas pequeñas. Los pequeños son conjuntos menores, y los grandes son los principales.

El significado de nuestra existencia, de acuerdo con la hipótesis generacional, es generar basura, por lo que el siguiente error es el miedo a crear basura.

La basura se puede crear cuando realmente es basura. , , , .

mark


V8 .


Stop the world, , JS, .

?


1 3%, .

3% = 1/33 GameDev. GameDev 3% 1 , . GameDev .

 const pool = [new Bullet(), new Bullet(), /* ... */]; function getFromPool() { const bullet = pool.find(x => !x.inUse); bullet.isUse = true; return bullet; } function returnToPool(bullet) { bullet.inUse = false; } // Frame const bullet = getFromPool(); // ... returnToPool(bullet); 

, , 10 000 .

— . , . , .

: Chromium


, , , Chromium.

 > performance.memory MemoryInfo { totalJSHeapSize: 10000000, usedJSHeapSize: 10000000, jsHeapSizeLimit: 2330000000 } 

Chromium performance.memory , , Chromium .

: Chromium 2 JavaScript.

, .

: Node


Node.js process.memoryUsage , .

 > process.memoryUsage() { rss: 22839296, heapTotal: 10207232, heapUsed: 5967968, external: 12829 } 

, - , . . .



— , . proposal , .

Node.js, c node-weak , , .

 let cached = new WeakRef(myJson); // 2   let json = cached.deref(); if (!json) { json = await fetchAgain(); } 

, , - JS. , , , .

WebAssembly , . , , , .

: v8.dev JS.


?



DevTools : Performance Memory . Chromium, , Firefox Safari .

Performance


Trace, «Memory» Performance, JS .



JS V8 , . . , GC 30 1200 JS, 1/40.

Memory


.



.



, . , , , V8 , . , .

, , Q ( compiled code) — React . , ?

, , , .

, .



, , , . , — 4 . , .



React, - : . , JSX.

Performance Memory , :

  • Chromium: about:tracing.
  • Firefox: about:memory about:performance, .
  • Node — trace-gc, —expose-gc, require('trace_events'). trace_events .

Resumen


  • , , , .
  • .
  • . , ?
  • , - .
  • SPA, , 1 , .
  • , - .

: flapenguin.me , Twitter , GitHub .

- ++ . YouTube-
.

, 2018 , . Frontend Conf 2018.

, :)

Source: https://habr.com/ru/post/es433318/


All Articles