Aprende a registrar instantáneas de montón con Memory > Profiles > Heap snapshot y encontrar fugas de memoria.
El Generador de perfiles de montón muestra la distribución de la memoria a través de los objetos de JavaScript de tu página y los nodos del DOM relacionados. Úsalo para tomar instantáneas del montón de JS, analizar gráficos de memoria, comparar instantáneas 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:
- En la página de la que quieras generar un perfil, abre Herramientas para desarrolladores y navega al panel Memoria.
- Selecciona el tipo de perfil radio_button_checked de instantánea del montón, selecciona una instancia de VM de JavaScript y haz clic en Take snapshot.
Cuando el panel Memory carga y analiza la instantánea, muestra el tamaño total de los objetos JavaScript accesibles debajo del título de la instantánea en la sección HEAP SNAPSHOTS.
Las instantáneas muestran solo los objetos del gráfico de la memoria a los que se puede acceder desde el objeto global. La toma de una instantánea siempre comienza con la recolección de elementos no utilizados.
Borrar instantáneas
Para quitar todas las instantáneas, haz clic en bloquear Borrar todos los perfiles:
Ver instantáneas
Si quieres inspeccionar las instantáneas desde diferentes perspectivas para diferentes propósitos, selecciona una de las vistas del menú desplegable en la parte superior:
View | Contenido | Objetivo |
---|---|---|
Resumen | Objetos agrupados por nombres de constructor | Úsalo para encontrar objetos y su uso de memoria en función del tipo. Útil para realizar un seguimiento de las filtraciones 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. Para confirmar la presencia y la causa de una fuga de memoria, inspecciona el delta en la memoria libre y el recuento de referencias. |
Contención | Contenido del montón | Proporciona una mejor vista de la estructura de los objetos y ayuda a analizar los objetos a los que se hace referencia en el espacio de nombres global (ventana) para encontrar lo que los mantiene. Úsalo para analizar cortes y revisar tus objetos en un nivel bajo. |
Estadísticas | Gráfico circular de la asignación de memoria | Observa los tamaños reales de las partes de la memoria asignadas a código, cadenas, arrays JS, arrays escritos y objetos del sistema. |
Vista de resumen
Inicialmente, se abrirá una instantánea del montón en la vista Summary que muestra Constructors en una columna. Puedes expandir los constructores para ver los objetos de los que crearon instancias.
Para filtrar constructores irrelevantes, escribe el nombre que quieras inspeccionar en el filtro Class en la parte superior de la vista Summary.
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:
- La Distancia muestra la distancia a la raíz con la ruta de acceso de nodos más corta y simple.
- Shallow size 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 retiene un objeto en sí. En general, los arrays y las cadenas tienen tamaños superficiales más grandes. Consulta también Tamaños de objetos.
- El tamaño retenido muestra el tamaño retenido máximo entre 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 ya no se pueda acceder a sus dependientes. Consulta también Tamaños de objetos.
Cuando expandes un constructor, la vista Summary muestra todas sus instancias. Cada instancia obtiene un desglose de sus tamaños superficiales y retenidos en las columnas correspondientes. El número después del carácter @
es el ID único del objeto. Te permite comparar instantáneas de montón por objeto.
Entradas especiales en Resumen
Además de agrupar por constructores, la vista Summary también agrupa objetos de la siguiente manera:
- Funciones integradas, como
Array
oObject
- Funciones que definiste en tu código.
- Categorías especiales que no se basan en constructores.
(array)
Esta categoría incluye varios objetos internos similares a arreglos que no se corresponden de forma directa con 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 facilitar el cambio de tamaño. Del mismo modo, las propiedades con nombre en los objetos JavaScript a menudo se almacenan en objetos internos secundarios llamados (object properties)[]
que también se enumeran 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ñas y lentas hasta grandes y rápidas.
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 a fin de que pueda ejecutarse más rápido. Si una función no se ha ejecutado por un tiempo, V8 puede borrar los datos internos de esa función.
(concatenated string)
Cuando V8 concatena dos strings, como con el operador +
de JavaScript, puede elegir representar el resultado internamente como una "cadena concatenada", también conocida como estructura de datos Rope.
En lugar de copiar todos los caracteres de las dos strings de origen en una nueva string, V8 asigna un objeto pequeño con campos internos llamados first
y second
, que apuntan a las dos strings 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 cadena.
InternalNode
Esta categoría representa objetos asignados fuera de V8, como objetos C++ definidos por Blink.
Para ver los nombres de clase C++, usa Chrome for Testing y haz lo siguiente:
- Abre Herramientas para desarrolladores y activa settings Configuración > Experimentos > check_box Mostrar opción para exponer componentes internos en instantáneas de montón.
- Abre el panel Memory, selecciona radio_button_checked Heap snapshot y activa check_box Exponer componentes internos (incluye detalles adicionales específicos de la implementación).
- 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 Propiedades rápidas de 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 debe tener una subcadena, como cuando el código JavaScript llama a String.prototype.substring()
, V8 puede asignar un objeto de cadena desglosada en lugar de copiar todos los caracteres relevantes de la cadena original. Este objeto nuevo contiene un puntero a la cadena original y describe el rango de caracteres de la cadena original que se usará.
Desde la perspectiva del código JavaScript, estas son solo cadenas normales y se comportan como cualquier otra cadena. Si una string fragmentada retiene mucha memoria, es posible que el programa haya activado el error 2869 y que se beneficie si toma medidas deliberadas para “compactar” la string fragmentada.
system / Context
Los objetos internos de tipo system / Context
contienen variables locales de un cierre, es decir, un alcance de JavaScript al que puede acceder una función anidada.
Cada instancia de función contiene un puntero interno hacia Context
en el que se ejecuta para que pueda acceder a esas variables. Si bien los objetos Context
no son visibles directamente desde JavaScript, tienes el control directo sobre ellos.
(system)
Esta categoría contiene varios objetos internos que no se han categorizado (aún) de manera más significativa.
Vista de comparación
En la vista Comparación, puedes encontrar objetos filtrados comparando varias instantáneas entre sí. Por ejemplo, realizar una acción y revertirla, como abrir un documento y cerrarlo, no debería dejar objetos adicionales atrás.
Sigue estos pasos para verificar que una operación determinada no cree fugas:
- Toma una instantánea del montón antes de realizar una operación.
- Realiza 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, hacer la interacción opuesta y repetirla varias veces.
- Toma una segunda instantánea de montón y cambia su vista a Comparison, y la compara con la Snapshot 1.
En la vista Comparison, se muestra la diferencia entre dos instantáneas. Cuando se expande una entrada de total, se muestran las instancias de objetos agregados y borrados:
Vista de contención
La vista Contención es una vista general de la estructura de objetos de tu aplicación. Te permite dar un vistazo a los cierres de funciones, observar los objetos internos de la VM que conforman tus objetos de JavaScript y comprender cuánta memoria usa tu aplicación 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. Raíces de GC utilizadas por el recolector de elementos no utilizados de la VM. Las raíces de GC pueden consistir en 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. Objetos del navegador "enviados" dentro de la máquina virtual de JavaScript para permitir la automatización, por ejemplo, nodos del DOM y reglas CSS.
La sección Contratos de servicio
En la sección Retainers, en la parte inferior del panel Memory, se muestran objetos que apuntan al objeto seleccionado en la vista. El panel Memory actualiza la sección Retainers cuando seleccionas objetos diferentes en cualquiera de las vistas, excepto Statistics.
En este ejemplo, la propiedad x
de una instancia Item
retiene la cadena seleccionada.
Ignorar contratos de servicio
Puedes ocultar los contratos de servicio para descubrir si otros objetos retienen el seleccionado. Con esta opción, no necesitas quitar primero este retenedor del código y, luego, volver a tomar la instantánea del montón.
Para ocultar un contrato de servicio, haz clic con el botón derecho y selecciona Ignorar este contrato de servicio. Los contratos de servicio omitidos se marcan como ignored
en la columna Distance. Para dejar de ignorar todos los contratos de servicio, haz clic en playlist_remove Restablecer contratos de servicio ignorados en la barra de acciones de la parte superior.
Buscar 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 los cierres
Es muy útil nombrar las funciones para poder distinguir cierres en la instantánea.
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 hace lo siguiente:
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 tiene la capacidad de reflejar dependencias bidireccionales entre objetos nativos del navegador (nodos del DOM y reglas de CSS) y los objetos de JavaScript. Esto ayuda a descubrir fugas que, de otra forma, serían invisibles debido a subárboles del DOM separados que flotan alrededor.
Las fugas del DOM pueden ser más grandes de lo que crees. Ten en cuenta el siguiente ejemplo. ¿Cuándo se recolecta el elemento no utilizado de #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
, todo el árbol debajo de #tree
es candidato para la recolección de elementos no utilizados.