Quería probar el uso de memoria para objetos en node.js. Mi enfoque fue simple: primero uso process.memoryUsage().heapUsed / 1024 / 1024 para obtener la memoria de referencia. Y tengo una matriz de tamaños, es decir, el número de entradas en objetos const WIDTHS = [100, 500, 1000, 5000, 10000] y planeo recorrerlo y crear objetos de ese tamaño y comparar el uso de memoria actual con el memoria base.
function memoryUsed() { const mbUsed = process.memoryUsage().heapUsed / 1024 / 1024 return mbUsed } function createObject(size) { const obj = {}; for (let i = 0; i < size; i++) { obj[Math.random()] = i; } return obj; } const SIZES = [100, 500, 1000, 5000, 10000, 50000, 100000, 500000, 1000000] const memoryUsage = {} function fn() { SIZES.forEach(size => { const before = memoryUsed() const obj = createObject(size) const after = memoryUsed() const diff = after - before memoryUsage[size] = diff }) } fn()pero los resultados no parecían correctos:
{ '100': 0.58087158203125, '500': 0.0586700439453125, '1000': 0.15680694580078125, '5000': 0.7640304565429688, '10000': 0.30365753173828125, '50000': 7.4157257080078125, '100000': 0.8076553344726562, } No tiene sentido. Además, dado que el objeto memoryUsage que registra el uso de la memoria en sí mismo ocupa más memoria a medida que crece, creo que agrega algo de sobrecarga.
¿Cuáles son algunas de las formas más sólidas y adecuadas de comparar el uso de la memoria en node.js?
La clave que falta es controlar que el recolector de basura se active en momentos "aleatorios". Para atribuir la memoria usada a acciones específicas realizadas, debe activar manualmente ejecuciones completas del GC antes de realizar una medición. Concretamente, modifique memoryUsed para que diga:
function memoryUsed() { gc(); const mbUsed = process.memoryUsage().heapUsed / 1024 / 1024; return mbUsed; } y ejecuta la prueba en Node con --expose-gc . Entonces obtendrás números razonables:
{ '100': 0.20072174072265625, '500': 0.0426025390625, '1000': 0.08499908447265625, '5000': 0.37823486328125, '10000': 0.7519683837890625, '50000': 4.9071807861328125, '100000': 9.80963134765625, '500000': 43.04571533203125, '1000000': 86.08901977539062 } El primer resultado (para 100 ) es obviamente demasiado alto; No estoy seguro de por qué (y si repito la prueba de 100 al final, su resultado está en línea con los demás). Los otros números se verifican: para un aumento de 2x o 5x en la cantidad de propiedades, el consumo de memoria aumenta aproximadamente en el mismo 2x o 5x.
Mi comentario de alto nivel es que no estoy seguro de lo que está tratando de medir aquí. La mayor parte de la memoria utilizada en este escenario se gasta en cadenas de la forma "0.38459934255705686" , mientras que su descripción parece indicar que está más interesado en los objetos.
El costo marginal de una propiedad de objeto depende del estado del objeto: cuando varios objetos comparten una forma/"clase oculta", entonces cada propiedad en un objeto toma solo un puntero (4 bytes en navegadores [plataformas de 32 bits o puntero compresión], 8 bytes en Nodo [64 bits sin compresión de puntero]). Cuando un objeto está en modo de diccionario, cada propiedad adicional tomará alrededor de 6 punteros en promedio (dependiendo de cuándo se debe aumentar el almacenamiento de respaldo del diccionario), por lo que 24 o 48 bytes dependiendo del tamaño del puntero. (Este último es el escenario que está creando esta prueba).
En ambos casos, este es solo el tamaño adicional del objeto que contiene la propiedad; el nombre y el valor de la propiedad, por supuesto, pueden necesitar memoria adicional.