Aprende a registrar instantáneas de montón con Memoria > Perfiles > Instantánea de montón y a encontrar fugas de memoria.
El generador de perfiles de montón muestra la distribución de la memoria según los objetos de JavaScript de tu página y los nodos DOM relacionados. Úsalo para tomar capturas de pantalla de montón de JS, analizar gráficos de memoria, comparar capturas de pantalla y encontrar fugas de memoria. Para obtener más información, consulta Árbol de retención de objetos.
Tomar una instantánea
Para tomar una instantánea del montón, haz lo siguiente:
- En una página de la que deseas generar un perfil, abre DevTools y navega al panel Memory.
- Selecciona el tipo de perfil de Instantánea de montón , luego selecciona una instancia de VM de JavaScript y haz clic en Tomar instantánea.
Cuando el panel Memoria carga y analiza la instantánea, muestra el tamaño total de los objetos de JavaScript accesibles debajo del título de la instantánea en la sección CAPTURAS DE PANTALLA DEL MONTÓN.
En las instantáneas, solo se muestran los objetos del gráfico de memoria a los que se puede acceder desde el objeto global. La toma de una captura de pantalla siempre comienza con una recolección de elementos no utilizados.
Borra instantáneas
Para quitar todas las capturas de pantalla, haz clic en
Borrar todos los perfiles:Ver instantáneas
Para inspeccionar instantáneas desde diferentes perspectivas y con diferentes fines, selecciona una de las vistas del menú desplegable en la parte superior:
Ver | Contenido | Objetivo |
---|---|---|
Resumen | Objetos agrupados por nombres de constructores. | Úsala para encontrar objetos y el uso que hacen de la memoria según el tipo. Es útil para hacer un seguimiento de las fugas del DOM. |
Comparación | Diferencias entre dos instantáneas | Úsala para comparar dos (o más) instantáneas, antes y después de una operación. Inspecciona el delta en la memoria liberada y el recuento de referencias para confirmar la presencia y la causa de una fuga de memoria. |
Contención | Contenido del montón | Proporciona una mejor vista de la estructura de objetos y ayuda a analizar los objetos a los que se hace referencia en el espacio de nombres global (ventana) para descubrir la razón por la cual no se borran. Úsala para analizar cierres y examinar los objetos en un nivel bajo. |
Estadísticas | Gráfico circular de la asignación de memoria | Consulta los tamaños relativos de las partes de memoria asignadas al código, las cadenas, los arrays de JS, los arrays tipados y los objetos del sistema. |
Vista de resumen
Inicialmente, se abre una instantánea del montón en la vista Resumen que muestra los Constructores en una columna. Puedes expandir los constructores para ver los objetos de los que crearon instancias.
Para filtrar los constructores irrelevantes, escribe el nombre que deseas inspeccionar en el Filtro de clase en la parte superior de la vista Resumen.
Los números junto a los nombres de los constructores indican la cantidad total de objetos creados con el constructor. La vista Resumen también muestra las siguientes columnas:
- En Distancia, se muestra la distancia a la raíz con la ruta de acceso de nodos más corta y simple.
- En Shallow size, se muestra la suma de tamaños superficiales de todos los objetos creados por un constructor determinado. El tamaño superficial es el tamaño de la memoria que un objeto retiene. Por lo general, las matrices y las cadenas tienen tamaños superficiales más grandes. Consulta también Tamaños de objetos.
- En Tamaño retenido, se muestra el tamaño retenido máximo en el mismo conjunto de objetos. El tamaño retenido es el tamaño de la memoria que puedes liberar si borras un objeto y haces que sus elementos dependientes ya no sean accesibles. Consulta también Tamaños de objetos.
Cuando expandes un constructor, la vista Resumen te muestra todas sus instancias. Cada instancia obtiene un desglose de sus tamaños superficiales y retenidos en las columnas correspondientes. El número que figura después del carácter @
es el ID único del objeto. Te permite comparar capturas de pantalla de montón por objeto.
Filtros de constructor
La vista Resumen te permite filtrar los constructores según casos comunes de uso ineficiente de la memoria.
Para usar estos filtros, selecciona una de las siguientes opciones en el menú desplegable de la derecha de la barra de acción:
- Todos los objetos: Todos los objetos capturados por la instantánea actual. Se establece de forma predeterminada.
- Objetos asignados antes de la instantánea 1: Son los objetos que se crearon y permanecieron en la memoria antes de que se tomara la primera instantánea.
- Objetos asignados entre las instantáneas 1 y 2: Consulta la diferencia en los objetos entre la instantánea más reciente y la anterior. Cada instantánea nueva agrega un incremento de este filtro a la lista desplegable.
- Cadena duplicada: Son valores de cadena que se almacenaron varias veces en la memoria.
- Objetos retenidos por nodos separados: Son objetos que se mantienen activos porque un nodo DOM separado hace referencia a ellos.
- Objetos retenidos por la consola de Herramientas para desarrolladores: Son objetos que se mantienen en la memoria porque se evaluaron o interactuaron con ellos a través de la consola de Herramientas para desarrolladores.
Entradas especiales en Resumen
Además de agrupar por constructores, la vista Resumen también agrupa objetos por los siguientes criterios:
- Funciones integradas, como
Array
oObject
- Elementos HTML agrupados por sus etiquetas, por ejemplo,
<div>
,<a>
,<img>
y otros. - Las funciones que definiste en tu código
- Son categorías especiales que no se basan en constructores.
(array)
Esta categoría incluye varios objetos internos similares a arrays que no corresponden directamente a objetos visibles en JavaScript.
Por ejemplo, el contenido de los objetos Array
de JavaScript se almacena en un objeto interno secundario llamado (object elements)[]
para permitir un cambio de tamaño más fácil. Del mismo modo, las propiedades con nombre en los objetos de JavaScript suelen almacenarse en objetos internos secundarios llamados (object properties)[]
que también se incluyen en la categoría (array)
.
(compiled code)
Esta categoría incluye datos internos que V8 necesita para ejecutar funciones definidas por JavaScript o WebAssembly. Cada función se puede representar de varias maneras, desde pequeña y lenta hasta grande y rápida.
V8 administra automáticamente el uso de memoria en esta categoría. Si una función se ejecuta muchas veces, V8 usa más memoria para esa función para que se ejecute más rápido. Si una función no se ejecuta desde hace un tiempo, es posible que V8 borre los datos internos de esa función.
(concatenated string)
Cuando V8 concatena dos cadenas, como con el operador +
de JavaScript, puede elegir representar el resultado de forma interna como una "cadena concatenada", también conocida como la estructura de datos Rope.
En lugar de copiar todos los caracteres de las dos cadenas de origen en una nueva, V8 asigna un objeto pequeño con campos internos llamados first
y second
, que apuntan a las dos cadenas de origen. Esto permite que V8 ahorre tiempo y memoria. Desde la perspectiva del código JavaScript, estas son solo cadenas normales y se comportan como cualquier otra.
InternalNode
Esta categoría representa objetos asignados fuera de V8, como los objetos C++ definidos por Blink.
Para ver los nombres de las clases de C++, usa Chrome for Testing y haz lo siguiente:
- Abre DevTools y activa Settings > Experiments > Show option to expose internals in heap snapshots.
- Abre el panel Memory, selecciona Heap snapshot y activa Expose internals (includes additional implementation-specific details).
- Reproduce el problema que causó que
InternalNode
retuviera mucha memoria. - Toma una instantánea del montón. En esta instantánea, los objetos tienen nombres de clase C++ en lugar de
InternalNode
.
(object shape)
Como se describe en Fast Properties in V8, V8 realiza un seguimiento de las clases ocultas (o formas) para que se puedan representar de manera eficiente varios objetos con las mismas propiedades en el mismo orden. Esta categoría contiene esas clases ocultas, llamadas system / Map
(no relacionadas con Map
de JavaScript), y los datos relacionados.
(sliced string)
Cuando V8 necesita tomar una subcadena, como cuando el código de JavaScript llama a String.prototype.substring()
, V8 puede optar por asignar un objeto cadena cortada en lugar de copiar todos los caracteres relevantes de la cadena original. Este objeto nuevo contiene un puntero a la cadena original y describe qué rango de caracteres de la cadena original se debe usar.
Desde la perspectiva del código JavaScript, estas son solo cadenas normales y se comportan como cualquier otra. Si una cadena cortada retiene mucha memoria, es posible que el programa haya activado el problema 2869 y que sea conveniente tomar medidas deliberadas para “aplanar” la cadena cortada.
system / Context
Los objetos internos de tipo system / Context
contienen variables locales de un cierre, un alcance de JavaScript al que puede acceder una función anidada.
Cada instancia de función contiene un puntero interno al Context
en el que se ejecuta para que pueda acceder a esas variables. Aunque los objetos Context
no son directamente visibles desde JavaScript, tienes control directo sobre ellos.
(system)
Esta categoría contiene varios objetos internos que aún no se categorizaron de una manera más significativa.
Vista de comparación
La vista Comparison te permite encontrar objetos filtrados comparando varias capturas de pantalla entre sí. Por ejemplo, realizar una acción y revertirla, como abrir un documento y cerrarlo, no debería dejar objetos adicionales.
Para verificar que una operación determinada no cree filtraciones, haz lo siguiente:
- Toma una instantánea del montón antes de realizar una operación.
- Realizar una operación Es decir, interactúa con una página de alguna manera que creas que podría estar causando una filtración.
- Realiza una operación inversa. Es decir, haz la interacción opuesta y repítela varias veces.
- Toma una segunda instantánea del montón y cambia su vista a Comparison, comparándola con la Instantánea 1.
En la vista Comparison, se muestra la diferencia entre dos capturas de pantalla. Cuando se expande una entrada de total, se muestran las instancias de los objetos agregados y borrados:
Vista de contención
La vista Containment es una "vista panorámica" de la estructura de objetos de tu aplicación. Te permite ver dentro de cierres de funciones, observar objetos internos de las VM que constituyen los objetos de JavaScript y comprender cuánta memoria usa tu app en un nivel muy bajo.
La vista proporciona varios puntos de entrada:
- Objetos DOMWindow. Objetos globales para el código JavaScript.
- Raíces de GC. Son las raíces de GC que usa el recolector de basura de la VM. Las raíces de GC pueden constar de mapas de objetos, tablas de símbolos, pilas de subprocesos de VM, cachés de compilación, alcances de controladores y controladores globales integrados.
- Objetos nativos. Son objetos del navegador que se insertan en la máquina virtual de JavaScript para permitir la automatización, por ejemplo, nodos del DOM y reglas de CSS.
La sección Retenedores
En la sección Retenedores, en la parte inferior del panel Memoria, se muestran los objetos que apuntan al objeto seleccionado en la vista. El panel Memoria actualiza la sección Retenedores cuando seleccionas objetos diferentes en cualquiera de las vistas, excepto Estadísticas.
En este ejemplo, la propiedad x
de una instancia de Item
retiene la cadena seleccionada.
Ignora los retenedores
Puedes ocultar los retenedores para saber si algún otro objeto retiene el seleccionado. Con esta opción, no tienes que quitar primero este retenedor del código y, luego, volver a tomar la instantánea del montón.
Para ocultar un retenedor, haz clic con el botón derecho y selecciona Ignorar este retenedor. Los retenedores omitidos se marcan como ignored
en la columna Distancia. Para dejar de ignorar todos los retenedores, haz clic en Restore ignored retainers en la barra de acciones de la parte superior.
Cómo encontrar un objeto específico
Para encontrar un objeto en el montón recopilado, puedes buscarlo con Ctrl + F y, luego, ingresar el ID del objeto.
Nombra las funciones para distinguir cierres
Resulta muy útil nombrar las funciones para que puedas distinguir entre cierres y capturas de pantalla.
Por ejemplo, el siguiente código no usa funciones con nombre:
function createLargeClosure() {
var largeStr = new Array(1000000).join('x');
var lC = function() { // this is NOT a named function
return largeStr;
};
return lC;
}
En este ejemplo, se realizan las siguientes acciones:
function createLargeClosure() {
var largeStr = new Array(1000000).join('x');
var lC = function lC() { // this IS a named function
return largeStr;
};
return lC;
}
Descubre fugas del DOM
El generador de perfiles de montón puede reflejar dependencias bidireccionales entre objetos nativos del navegador (nodos del DOM y reglas de CSS) y objetos de JavaScript. Esto ayuda a descubrir fugas que, de otro modo, serían invisibles y que se producen debido a subárboles del DOM separados que quedan dando vueltas.
Las fugas del DOM pueden ser más grandes de lo que crees. Considera el siguiente ejemplo: ¿Cuándo se recolecta el elemento no utilizado #tree
?
var select = document.querySelector;
var treeRef = select("#tree");
var leafRef = select("#leaf");
var body = select("body");
body.removeChild(treeRef);
//#tree can't be GC yet due to treeRef
treeRef = null;
//#tree can't be GC yet due to indirect
//reference from leafRef
leafRef = null;
//#NOW #tree can be garbage collected
#leaf
mantiene una referencia a su elemento superior (parentNode
) y, de manera recursiva, hasta #tree
, por lo que solo cuando se anula leafRef
, el árbol completo debajo de #tree
es un candidato para la GC.