Administración de memoria JavaScript y recolección de basura

Los lenguajes de bajo nivel como C generalmente tienen interfaces de administración de memoria de bajo nivel, como malloc () y free (). Por el contrario, JavaScript tiene un mecanismo automático de recolección de basura. El entorno de ejecución es responsable de administrar la memoria utilizada durante la ejecución del código. La memoria se asigna automáticamente cuando se crean variables (objetos, cadenas, etc.) y se libera "automáticamente" cuando no se utilizan. El proceso de liberación se llama recolección de basura. Este mecanismo de recolección de basura es muy simple, encuentre las variables que no están en uso y luego libere la memoria que ocupan. Para este fin, el recolector de basura realizará periódicamente esta operación a intervalos fijos (o el tiempo de recolección programado durante la ejecución del código).

1. El ciclo de vida de las variables locales.

Las variables locales solo existen durante la ejecución de la función. En este proceso, el espacio correspondiente se asignará en la memoria de la pila (o montón) para las variables locales con el fin de almacenar sus valores. Luego use estas variables en la función hasta el final de la ejecución de la función. Después del final, las variables locales ya no se utilizan, por lo que no hay necesidad de existir, por lo que libera su memoria para su uso futuro.

2. Ciclo de vida de la memoria.

No importa qué lenguaje de programación, el ciclo de vida de la memoria es básicamente el mismo:

  1. Asigna la memoria que necesitas
  2. Usar memoria asignada (lectura, escritura)
  3. Liberar \ volver cuando no sea necesario
Asignación de memoria de JavaScript
2.1. Inicialización de valores

Para evitar que los programadores asignen memoria, JavaScript completa la asignación de memoria al definir variables.

var n = 123; // 给数值变量分配内存var s = "azerty"; // 给字符串分配内存
var o = {
  a: 1,
  b: null
};
// 给对象及其包含的值分配内存
// 给数组及其包含的值分配内存(就像对象一样)var a = [1, null, "abra"]; 
function f(a){
  return a + 2;
} 
// 给函数(可调用的对象)分配内存
// 函数表达式也能分配一个对象
someElement.addEventListener('click', function(){
  someElement.style.backgroundColor = 'blue';
}, false);
2.2 Asignación de memoria a través de llamadas a funciones

El resultado de algunas llamadas a funciones es asignar memoria de objeto:

var d = new Date(); // 分配一个 Date 对象
var e = document.createElement('div'); // 分配一个 DOM 元素

Algunos métodos asignan nuevas variables o nuevos objetos:

// s2 是一个新的字符串
// 因为字符串是不变量
// JavaScript 可能决定不分配内存
// 只是存储了 [0-3] 的范围。
var s = "azerty";
var s2 = s.substr(0, 3); 
// 新数组有四个元素,是 a 连接 a2 的结果
var a = ["ouais ouais", "nan nan"];var a2 = ["generation", "nan nan"];var a3 = a.concat(a2); 
2.3. Valor de uso

El proceso de usar valores es en realidad la operación de leer y escribir memoria asignada. Leer y escribir puede ser escribir una variable o un valor de atributo de un objeto, o incluso pasar parámetros de una función.

2.4. Libre cuando ya no se necesita memoria

La mayoría de los problemas de administración de memoria se encuentran en esta etapa. La tarea más difícil aquí es encontrar "qué memoria asignada realmente ya no es necesaria". A menudo requiere que los desarrolladores determinen qué bloque de memoria en el programa ya no es necesario y lo liberen.
El intérprete de idiomas de alto nivel incorpora un "recolector de basura", su trabajo principal es rastrear la asignación y el uso de la memoria, de modo que cuando la memoria asignada ya no se usa, se libera automáticamente. Esto solo puede ser un proceso aproximado, porque es imposible determinar si todavía se necesita un bloque de memoria (no puede ser resuelto por un algoritmo).

3. Recolección de basura

No se puede determinar el problema de encontrar automáticamente si "ya no se necesita algo de memoria" como se describió anteriormente. Por lo tanto, la recolección de basura solo puede resolver los problemas generales con limitaciones. Esta sección explicará los conceptos necesarios y comprenderá los principales algoritmos de recolección de basura y sus limitaciones.

3.1. Referencia

El algoritmo de recolección de basura se basa principalmente en el concepto de referencia. En el contexto de la gestión de memoria, si un objeto tiene permiso para acceder a otro objeto (implícito o explícito), se llama que un objeto se refiere a otro objeto. Por ejemplo, un objeto Javascript tiene una referencia a su prototipo (referencia implícita) y una referencia a sus atributos (referencia explícita).
Aquí, el concepto de "objeto" se refiere no solo a los objetos de JavaScript, sino también al alcance de la función (o alcance léxico global).

3.2 Recolección de basura de conteo de referencia

Este es el algoritmo inicial de recolección de basura. Este algoritmo define "si el objeto ya no es necesario" como "si el objeto hace referencia a él". Si no hay puntos de referencia para el objeto (referencia cero), el objeto será recolectado por el mecanismo de recolección de basura. El significado es rastrear el número de veces que se hace referencia a cada valor. Cuando se declara una variable y se le asigna un valor de tipo de referencia a la variable, el número de citas es 1, si el valor se asigna a otra variable, entonces referencia El número de veces es +1. Por el contrario, si la variable que contiene este valor recibe otro valor, el número de veces que se cita el valor es -1. Cuando el valor de este valor es 0, significa que no hay ninguna variable para usar este valor, por lo que Recupera el espacio de memoria que ocupa. Cuando el recolector de basura se ejecuta nuevamente, es para liberar el espacio ocupado por los valores con cero tiempos de referencia.

// 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量o
// 很显然,没有一个可以被垃圾收集
var o = { 
  a: {
    b:2
  }}; 
  
// o2变量是第二个对“这个对象”的引用
var o2 = o; 

// 现在,“这个对象”只有一个o2变量的引用了,“这个对象”的原始引用o已经没有
o = 1; 

// 引用“这个对象”的a属性
// 现在,“这个对象”有两个引用了,一个是o2,一个是oa
var oa = o2.a; 
               
// 虽然最初的对象现在已经是零引用了,可以被垃圾回收了
// 但是它的属性a的对象还在被oa引用,所以还不能回收
o2 = "yo"; 

// a属性的那个对象现在也是零引用了
// 它可以被垃圾回收了
oa = null; 
3.2.1 Restricciones: referencias circulares

Netspace Navigator3.0 utilizó el conteo de referencias lo antes posible, pero pronto encontró un problema grave: la referencia circular referencia
circular se refiere al objeto A contiene un puntero al objeto B, y el objeto B también contiene un puntero al objeto A .
El algoritmo tiene una limitación: no puede manejar referencias circulares. En el siguiente ejemplo, dos objetos son creados y referenciados entre sí, formando un ciclo. Después de que se los llame, dejarán el alcance de la función, por lo que ya no son útiles y pueden reciclarse. Sin embargo, el algoritmo de conteo de referencia considera que todos se refieren entre sí al menos una vez, por lo que no se reciclarán.

function f(){
  var objectA = new Object();
  var objectB = new Object();
  objectA .a = objectB ; // o 引用 o2
  objectB .a = objectA ; // o2 引用 o
}
f();

En este ejemplo, oA y oB se refieren entre sí, es decir, el número de referencias a ambos objetos es 2, cuando se adopta la estrategia de conteo, oA y oB continuarán existiendo después de ejecutar la función. Provoca que no se recicle una gran cantidad de memoria, porque su número de referencias nunca será 0, pero la estrategia de marcado no es un problema

3.2.2 Ejemplos prácticos

IE 6, 7 utiliza el recuento de referencias para recolectar basura los objetos DOM. Este método a menudo causa pérdidas de memoria cuando se hace referencia circular al objeto:

var div;
window.onload = function(){
  div = document.getElementById("myDivElement");
  div.circularReference = div;
  div.lotsOfData = new Array(10000).join("*");
};

En el ejemplo anterior, el atributo circularReference del elemento DOM myDivElement hace referencia a myDivElement, lo que da como resultado una referencia circular. Si el atributo no se elimina explícitamente o se establece en nulo, el recolector de basura contado por referencia siempre tendrá al menos una referencia y mantendrá el elemento DOM en la memoria, incluso si se elimina del árbol DOM. Si este elemento DOM tiene una gran cantidad de datos (el atributo lotOfData anterior), la memoria ocupada por estos datos nunca se liberará.

3.3. Algoritmo de marca-barrido

Este algoritmo simplemente define "si el objeto ya no es necesario" como "si el objeto está disponible". ,
Cuando la variable en el medio ambiente, esta variable se marcará como "en el medio ambiente", por lo general no puede ser liberado al medio ambiente ocupaba la memoria de variables, siempre y cuando el flujo de ejecución en el entorno que se ajuste, es posible utilizarlo. Cuando la variable abandone el entorno, márquela como "Abandonar el entorno".

3.3.1 Método de marcado:

Puede usar cualquier método para marcar variables, por ejemplo, puede voltear un bit especial para registrar cuando una variable ingresa al entorno, o usar una lista de variables de "ingresar al entorno" y una lista de variables de "salir del entorno" para rastrear qué variable Ha cambiado Sin embargo, esto no es importante, la clave está en la estrategia adoptada.

El recolector de basura marcará todas las variables almacenadas en la memoria en tiempo de ejecución y eliminará las etiquetas de las variables en el entorno y las variables a las que hacen referencia las variables en el entorno. Las variables marcadas después de este tiempo se tratarán como variables a eliminar, porque las variables en el entorno ya no pueden acceder a estas variables. Finalmente, el recolector de basura completa el trabajo de limpieza de memoria, destruye los valores marcados y recupera el espacio de memoria ocupado por ellos.

Este algoritmo supone que se establece un objeto llamado raíz (en Javascript, la raíz es un objeto global). El recolector de basura comenzará periódicamente desde la raíz, encontrará todos los objetos a los que se hace referencia desde la raíz y luego encontrará los objetos a los que hacen referencia estos objetos ... Desde la raíz, el recolector de basura encontrará todos los objetos que pueden obtenerse y recolectará todos los objetos que no pueden obtenerse.

Este algoritmo es mejor que el anterior, porque los "objetos con cero referencias" no siempre están disponibles, pero lo contrario no es necesariamente el caso, consulte "referencias cíclicas".
Desde 2012, todos los navegadores modernos han utilizado el algoritmo de recolección de basura de marcado y barrido. Todas las mejoras en el algoritmo de recolección de basura de JavaScript se basan en la mejora del algoritmo de marcado y barrido, y no existe una mejora del algoritmo de marcado y barrido en sí y su definición simplificada de "si el objeto ya no es necesario".

3.3.2 Las referencias circulares ya no son un problema

En el ejemplo anterior, después de que vuelve la llamada a la función, los dos objetos no se pueden obtener del objeto global. Por lo tanto, serán reciclados por el recolector de basura. El segundo ejemplo es el mismo: una vez que el div y su manejo de eventos no pueden obtenerse desde la raíz, el recolector de basura los recogerá.

3.3.3 Restricciones: los objetos que no se pueden consultar desde el objeto raíz se borrarán

Aunque esto es una limitación, en la práctica rara vez encontramos situaciones similares, por lo que los desarrolladores están menos preocupados por el mecanismo de recolección de basura.

4. Problemas de rendimiento

En todos los navegadores, el proceso de recolección de basura se puede activar, pero no se recomienda. En IE, llame al método window.CollectGarbage () realizará inmediatamente la recolección de basura. En Opera 7 y versiones posteriores, llamar a window.opera.collect () también inicia una rutina de recolección de basura.

5. Gestión de memoria

JavaScript generalmente asigna menos memoria disponible a los navegadores web que las aplicaciones de escritorio. Esto se hace por razones de seguridad, el propósito es evitar que la página web de JavaScript se quede sin memoria del sistema y que el sistema se bloquee. El problema del límite de memoria no solo afecta la asignación de memoria a las variables, sino que también afecta la pila de llamadas y el número de declaraciones que se pueden ejecutar simultáneamente en un hilo.
Asegurarse de que se utiliza la menor cantidad de memoria permite que las páginas obtengan un mejor rendimiento. La mejor manera de optimizar el uso de la memoria es guardar solo los datos necesarios para el código en ejecución. Una vez que los datos ya no son útiles, es mejor liberarlos configurándolos como nulos; esta práctica se denomina desreferenciación. Este enfoque se aplica a la mayoría de las variables y propiedades globales de los objetos globales. Las variables locales se desreferencian automáticamente cuando salen del entorno de ejecución.

funtion createPerson(name){
  var localPerson = new Object();
  localPerson.name = name;
  return localPerson;
} 
Var globalPerson = createPerson(‘Nicholas’);
// 手工解除globalPerson 的引用
globalPerson = null;

Anular la referencia a un valor no significa que la memoria ocupada por el valor se recupere automáticamente. El efecto real de la desreferenciación es obtener el valor del entorno de ejecución para que el recolector de basura pueda reciclarlo la próxima vez que se ejecute.

Referencia:
[1]: https: //developer.mozilla.org/zh-CN/docs/Web/JavaScript/Memory_Management
[2]: programación avanzada de JavaScript (3a edición) 78-81

5 artículos originales publicados · Me gusta0 · Visitas 125

Supongo que te gusta

Origin blog.csdn.net/forteenBrother/article/details/105352944
Recomendado
Clasificación