La recolección de basura es un mecanismo oculto de JavaScript
. Por lo general, no necesitamos preocuparnos por la recolección de basura, solo debemos concentrarnos en el desarrollo de funciones. Pero esto no significa que podamos sentarnos y relajarnos al escribir JavaScript
. A medida que las funciones que implementamos se vuelven cada vez más complejas y la cantidad de código se acumula, los problemas de rendimiento se vuelven cada vez más prominentes. Cómo escribir código que se ejecute más rápido y ocupe menos memoria es la búsqueda incesante de los programadores. Un excelente programador siempre puede lograr resultados sorprendentes con recursos extremadamente limitados. Ésta es también la diferencia entre seres ordinarios y dioses distantes.
? Al ejecutarlo en la memoria de la computadora, todas las variables, objetos y funciones que definimos en el código ocuparán una cierta cantidad de espacio de memoria en la memoria. En las computadoras, el espacio de memoria es un recurso muy escaso. Siempre debemos prestar atención al uso de la memoria. Después de todo, ¡los módulos de memoria son muy caros! Una variable, función u objeto puede denominarse basura si ya no es necesario para la ejecución posterior del código después de su creación.
Aunque es muy fácil entender intuitivamente la definición de basura, para un programa de computadora nos resulta difícil concluir en un momento determinado que las variables, funciones u objetos actualmente existentes ya no se utilizarán en el futuro. Para reducir el costo de la memoria de la computadora y garantizar la ejecución normal de los programas de computadora, generalmente estipulamos que los objetos o variables que cumplan cualquiera de las siguientes condiciones son basura:
las variables u objetos que no están referenciados equivalen a una casa sin puerta. Nunca podremos ingresar a ella, por lo que es imposible usarlas. Aunque los objetos inaccesibles estén conectados, siguen siendo inaccesibles desde el exterior y, por lo tanto, no se pueden volver a utilizar. Los objetos o variables que cumplan las condiciones anteriores nunca se volverán a utilizar en la ejecución futura del programa, por lo que pueden tratarse de forma segura como recolección de basura.
Cuando aclaramos los objetos que deben descartarse mediante la definición anterior, ¿significa que no hay basura en las variables y objetos restantes?
¡No! La basura que identificamos actualmente es solo una parte de toda la basura. Aún habrá otra basura que no cumpla con las condiciones anteriores, pero no se volverá a utilizar.
¿Se puede decir que la basura que cumple con la definición anterior es "basura absoluta" y otra basura oculta en el programa es "basura relativa"?
El mecanismo de recolección de basura ( GC,Garbage Collection
) es responsable de reciclar las variables inútiles y el espacio de memoria ocupado durante la ejecución del programa. El fenómeno de que un objeto todavía exista en la memoria aunque no tenga posibilidad de ser utilizado nuevamente se llama pérdida de memoria . Las pérdidas de memoria son un fenómeno muy peligroso, especialmente en programas de larga duración. Si un programa tiene una pérdida de memoria, ocupará cada vez más espacio de memoria hasta que se quede sin memoria.
Las cadenas, los objetos y las matrices no tienen un tamaño fijo, por lo que la asignación de almacenamiento dinámico para ellos solo se puede realizar si se conoce su tamaño. Cada vez que un programa JavaScript crea una cadena, matriz u objeto, el intérprete asigna memoria para almacenar la entidad. Siempre que la memoria se asigna dinámicamente de esta manera, eventualmente debe liberarse para poder usarse nuevamente, de lo contrario, el intérprete de JavaScript consumirá toda la memoria disponible en el sistema, provocando que el sistema falle;
El mecanismo de recolección de basura de JavaScript
buscará intermitentemente variables y objetos inútiles (basura) y liberará el espacio que ocupan.
Los diferentes lenguajes de programación adoptan diferentes estrategias de recolección de basura. Por ejemplo, C++
no tiene un mecanismo de recolección de basura. Toda la administración de la memoria depende de las propias habilidades del programador, lo que hace que C++
sea más difícil de dominar. JavaScript
usa la accesibilidad para administrar la memoria. Literalmente, la accesibilidad significa accesible, lo que significa que el programa puede acceder y utilizar variables y objetos de alguna manera.
JavaScript
especifica un conjunto inherente de valores alcanzables, y los valores del conjunto son inherentemente accesibles:
se denominan raíces , que son los nodos superiores del árbol de accesibilidad.
Una variable u objeto se considera accesible si la variable raíz lo utiliza directa o indirectamente.
En otras palabras, un valor es accesible si se puede alcanzar a través de la raíz (por ejemplo, Abcde
).
let people = { chicos:{ chicos1:{nombre:'xiaoming'}, chicos2:{nombre:'xiaojun'}, }, chicas:{ chicas1:{nombre:'xiaohong'}, chicas2:{nombre:'huahua'}, }};
El código anterior crea un objeto y lo asigna a la variable people
. La variable people
contiene dos objetos, boys
y girls
, y boys
y girls
contienen dos subobjetos respectivamente. Esto también crea una estructura de datos que contiene 3
niveles de relaciones de referencia (independientemente del tipo de datos básico), como se muestra a continuación:
Entre ellos, el nodo people
es naturalmente accesible porque es una variable global. Los nodos boys
y girls
son accesibles indirectamente porque están referenciados directamente por variables globales. boys1
, boys2
, girls1
y girls2
también son variables accesibles porque las variables globales las utilizan indirectamente y se puede acceder a ellas a través de people.boys.boys
.
Si agregamos el siguiente código después del código anterior:
people.girls.girls2 = null; people.girls.girls1 = people.boys.boys2
Entonces, el diagrama de jerarquía de referencia anterior quedará como sigue:
Entre ellos, girls1
y girls2
se convirtieron en nodos inalcanzables debido a la desconexión del nodo grils
, lo que significa que serán recicladas por el mecanismo de recolección de basura.
Y si en este momento ejecutamos el siguiente código:
people.boys.boys2 = null
entonces el diagrama de jerarquía de referencia quedará como sigue:
En este momento, aunque el nodo boys
y el nodo boys2
están desconectados, debido a la relación de referencia entre boys2
y girls
, todavía se puede acceder boys2
y el mecanismo de recolección de basura no lo reciclará.
El diagrama de asociación anterior demuestra por qué el valor equivalente de la variable global se llama raíz , porque en el diagrama de asociación, este tipo de valor generalmente aparece como el nodo raíz del árbol de relaciones.
let people = { chicos:{ chicos1:{nombre:'xiaoming'}, chicos2:{nombre:'xiaojun'}, }, chicas:{ chicas1:{nombre:'xiaohong'}, chicas2:{nombre:'huahua'}, }};personas.chicos.chicos2.novia = personas.chicas.chicas1; //boys2 se refiere a girls1people.girls.girls1.boyfriend = people.boys.boys2; //girls1 se refiere a boys2
El código anterior crea una relación interrelacionada entre boys2
y girls1
.
En este punto, si cortamos la asociación entre boys
y boys2
:
eliminar personas.chicos.chicos2
el diagrama de asociación entre objetos es el siguiente:
Evidentemente, no existen nodos inalcanzables.
En este punto, si cortamos la conexión de la relación boyfriend
:
elimine people.girls.girls1,
el diagrama de relación se convierte en:
En este momento, aunque todavía existe una relación girlfriend
entre boys2
y girls1
, boys2
se convierte en un nodo inalcanzable y será reclamado por el mecanismo de recolección de basura.
let people = { chicos:{ chicos1:{nombre:'xiaoming'}, chicos2:{nombre:'xiaojun'}, }, chicas:{ chicas1:{nombre:'xiaohong'}, chicas2:{nombre:'huahua'}, }};delete people.boys;delete people.girls;
El diagrama de jerarquía de referencia formado por el código anterior es el siguiente:
En este momento, aunque todavía existe una relación de referencia mutua entre los objetos dentro del cuadro de puntos, estos objetos tampoco son accesibles y serán eliminados por el mecanismo de recolección de basura. Estos nodos han perdido su relación con la raíz y se vuelven inalcanzables.
El llamado recuento de referencias, como su nombre indica, cuenta cada vez que se hace referencia a un objeto. Agregar una referencia la aumentará en uno y eliminar una referencia la disminuirá en uno. El número se convierte en 0, se considera basura, por lo que se eliminan objetos para recuperar memoria.
Por ejemplo:
let usuario = {nombre de usuario:'xiaoming'}; // La variable de usuario hace referencia al objeto, cuenta +1 dejar usuario2 = usuario; // El objeto es referenciado por una nueva variable y el recuento +1 usuario = nulo; //La variable ya no hace referencia al objeto, el recuento es -1 usuario2 = nulo; //La variable ya no hace referencia al objeto, número impar -1 // En este momento, el número de referencias de objetos es 0 y se eliminará.
Aunque el método de recuento de referencias parece muy razonable, de hecho, existen lagunas obvias en el mecanismo de reciclaje de memoria que utiliza el método de recuento de referencias.
Por ejemplo:
let chico = {}; dejar niña = {}; chico.novia = chica; chica.novio = chico; chico = nulo; girl = null;
El código anterior tiene referencias mutuas entre boy
y girl
. El conteo elimina las referencias en boy
y girl
, y los dos objetos no se reciclarán. Debido a la existencia de referencias circulares, los recuentos de referencias de los dos objetos anónimos nunca volverán a cero, lo que provocará una pérdida de memoria.
Existe un concepto de puntero inteligente ( shared_ptr
) en C++
. Los programadores pueden usar el puntero inteligente para usar el destructor de objetos para liberar el recuento de referencias. Sin embargo, se producirán pérdidas de memoria en el caso de referencias circulares.
Afortunadamente, JavaScript
ha adoptado otra estrategia más segura, que evita en mayor medida el riesgo de pérdidas de memoria.
Marcar mark and sweep
es un algoritmo de recolección de basura adoptado por el motor JavaScript
. Su principio básico es comenzar desde la raíz , recorrer primero la relación de referencia entre variables y poner una marca (优秀员工徽章
) en las variables atravesadas. Los objetos no marcados finalmente se eliminan.
El proceso básico del algoritmo es el siguiente:
2
hasta que no se unan nuevos empleados Excelentes;Por ejemplo:
si hay una relación de referencia de objeto en nuestro programa como se muestra a continuación:
Podemos ver claramente que hay una "isla accesible" en el lado derecho de toda la imagen. Comenzando desde la raíz , nunca se puede llegar a la isla. Pero el recolector de basura no tiene una perspectiva divina como la nuestra. Solo marcarán el nodo raíz como un empleado sobresaliente según el algoritmo.
Luego comience con los empleados destacados y busque todos los nodos citados por los empleados destacados, como los tres nodos en el cuadro de puntos de la figura anterior. Luego marque los nodos recién encontrados como empleados destacados.
El proceso de búsqueda y marcado se repite hasta que todos los nodos que se pueden encontrar se marcan con éxito.
Finalmente, se logra el efecto que se muestra en la siguiente figura:
Dado que las islas de la derecha aún no están marcadas después de que finaliza el ciclo de ejecución del algoritmo, la tarea del recolector de basura no podrá alcanzar estos nodos y eventualmente se borrarán.
Los niños que han estudiado estructuras de datos y algoritmos pueden sorprenderse al descubrir que se trata de un recorrido de gráficos, similar a los algoritmos de gráficos conectados.
La recolección de basura es una tarea a gran escala, especialmente cuando la cantidad de código es muy grande, la ejecución frecuente del algoritmo de recolección de basura reducirá significativamente la ejecución del programa. El algoritmo JavaScript
ha realizado muchas optimizaciones en la recolección de basura para garantizar que el programa se pueda ejecutar de manera eficiente y al mismo tiempo garantizar la ejecución normal del trabajo de reciclaje.
Las estrategias adoptadas para la optimización del rendimiento generalmente incluyen los siguientes puntos:
Los programas JavaScriptJavaScript
una cantidad considerable de variables durante la ejecución, y el escaneo frecuente de estas variables causará una sobrecarga significativa. Sin embargo, estas variables tienen sus propias características en el ciclo de vida. Por ejemplo, las variables locales a menudo se crean, se usan rápidamente y luego se descartan, mientras que las variables globales ocupan memoria durante mucho tiempo. JavaScript
administra los dos tipos de objetos por separado. Para las variables locales que se crean, usan y descartan rápidamente, el recolector de basura escaneará con frecuencia para garantizar que estas variables se limpien rápidamente después de que pierdan su uso. Para las variables que mantienen memoria durante mucho tiempo, reduzca la frecuencia de verificación, ahorrando así una cierta cantidad de gastos generales.
La idea incremental es muy común en la optimización del rendimiento y también se puede utilizar para la recolección de basura. Cuando el número de variables es muy grande, obviamente lleva mucho tiempo recorrer todas las variables a la vez y emitir calificaciones sobresalientes a los empleados, lo que provoca retrasos durante la ejecución del programa. Por lo tanto, el motor dividirá el trabajo de recolección de basura en múltiples subtareas y ejecutará gradualmente cada pequeña tarea durante la ejecución del programa. Esto provocará un cierto retraso en la recuperación, pero generalmente no provocará retrasos obvios en el programa.
CPU
siempre funciona incluso en programas complejos. Esto se debe principalmente a que CPU
funciona muy rápido y IO
periférica suele ser varios órdenes de magnitud más lenta. Por lo tanto, es una buena idea organizar una estrategia de recolección de basura cuando CPU
está inactiva. inactivo. Este es un método de optimización del rendimiento muy eficaz y básicamente no tendrá ningún efecto adverso en el programa en sí. Esta estrategia es similar a la actualización del tiempo de inactividad del sistema y los usuarios no son conscientes de la ejecución en segundo plano.
La tarea principal de este artículo es simplemente finalizar los mecanismos de recolección de basura, las estrategias comúnmente utilizadas y los métodos de optimización. No pretende brindarles a todos una comprensión profunda de los principios de ejecución en segundo plano del motor.
A través de este artículo, debe comprender:
JavaScript
, que se ejecuta en segundo plano y no requiere que nos preocupemos por ello.