Maison > interface Web > js tutoriel > Quelle est exactement la limite de mémoire de Node.js ?

Quelle est exactement la limite de mémoire de Node.js ?

Susan Sarandon
Libérer: 2024-12-25 04:35:18
original
847 Les gens l'ont consulté

What Exactly Is the Memory Limit of Node.js?

La maîtrise de l'API Node.js peut vous permettre d'avancer rapidement, mais une compréhension approfondie de l'empreinte mémoire des programmes Node.js peut vous amener plus loin.

Commençons par jeter un coup d'œil à notre utilisation de la mémoire avec process.memoryUsage(), en mettant à jour toutes les secondes :

setInterval(() => { console.log('Memory Usage:', process.memoryUsage()); }, 1000);
Copier après la connexion
Copier après la connexion

Étant donné que la sortie est en octets, elle n'est pas conviviale. Améliorons-le en formatant l'utilisation de la mémoire en Mo :

function formatMemoryUsageInMB(memUsage) {
    return {
        rss: convertToMB(memUsage.rss),
        heapTotal: convertToMB(memUsage.heapTotal),
        heapUsed: convertToMB(memUsage.heapUsed),
        external: convertToMB(memUsage.external)
    };
}

const convertToMB = value => {
    return (value / 1024 / 1024).toFixed(2) + ' MB';
};

const logInterval = setInterval(() => {
    const memoryUsageMB = formatMemoryUsageInMB(process.memoryUsage());
    console.log(`Memory Usage (MB):`, memoryUsageMB);
}, 1000);
Copier après la connexion
Copier après la connexion

Maintenant, nous pouvons obtenir le résultat suivant chaque seconde :

Memory Usage (MB): {
  rss: '30.96 MB', // The actual OS memory used by the entire program, including code, data, shared libraries, etc.
  heapTotal: '6.13 MB', // The memory area occupied by JS objects, arrays, etc., dynamically allocated by Node.js
                      // V8 divides the heap into young and old generations for different garbage collection strategies
  heapUsed: '5.17 MB',
  external: '0.39 MB'
}

Memory Usage (MB): {
  rss: '31.36 MB',
  heapTotal: '6.13 MB',
  heapUsed: '5.23 MB',
  external: '0.41 MB'
}
Copier après la connexion

Nous savons tous que l'utilisation de la mémoire du moteur V8 est limitée, non seulement par les politiques de gestion de la mémoire et d'allocation des ressources du système d'exploitation, mais également par ses propres paramètres.

En utilisant os.freemem(), nous pouvons voir la quantité de mémoire libre dont dispose le système d'exploitation, mais cela ne signifie pas que tout est à gagner par un programme Node.js.

console.log('Free memory:', os.freemem());
Copier après la connexion

Pour les systèmes 64 bits, la taille maximale par défaut de l'ancien espace de Node.js V8 est d'environ 1,4 Go. Cela signifie que même si votre système d'exploitation dispose de plus de mémoire disponible, le V8 n'utilisera pas automatiquement plus que cette limite.

Astuce : Cette limite peut être modifiée en définissant des variables d'environnement ou en spécifiant des paramètres lors du démarrage de Node.js. Par exemple, si vous souhaitez que V8 utilise un tas plus grand, vous pouvez utiliser l'option --max-old-space-size :

node --max-old-space-size=4096 your_script.js
Copier après la connexion

Cette valeur doit être définie en fonction de votre situation et de votre scénario réels. Par exemple, si vous disposez d'une machine avec beaucoup de mémoire, déployée de manière autonome, et que vous disposez de nombreuses machines à petite mémoire déployées de manière distribuée, le paramètre de cette valeur sera définitivement différent.

Exécutons un test en remplissant un tableau de données indéfiniment jusqu'à ce que la mémoire déborde et voyons quand cela se produit.

const array = [];
while (true) {
    for (let i = 0; i < 100000; i++) {
        array.push(i);
    }
    const memoryUsageMB = formatMemoryUsageInMB(process.memoryUsage());
    console.log(`Memory Usage (MB):`, memoryUsageMB);
}
Copier après la connexion

C'est ce que nous obtenons lorsque nous exécutons le programme directement. Après avoir ajouté un peu de données, le programme plante.

Memory Usage (MB): {
  rss: '2283.64 MB',
  heapTotal: '2279.48 MB',
  heapUsed: '2248.73 MB',
  external: '0.40 MB'
}
Memory Usage (MB): {
  rss: '2283.64 MB',
  heapTotal: '2279.48 MB',
  heapUsed: '2248.74 MB',
  external: '0.40 MB'
}


#
# Fatal error in , line 0
# Fatal JavaScript invalid size error 169220804
#
#
#
#FailureMessage Object: 0x7ff7b0ef8070
Copier après la connexion

Vous êtes confus ? La limite n'est-elle pas 1,4G ? Pourquoi utilise-t-il la 2G ? En fait, la limite de 1,4 Go de Node.js est une limite historique du moteur V8, applicable aux premières versions du V8 et à certaines configurations. Dans Node.js et V8 modernes, Node.js ajuste automatiquement son utilisation de la mémoire en fonction des ressources système. Dans certains cas, il peut utiliser bien plus de 1,4 Go, en particulier lorsqu'il s'agit de grands ensembles de données ou d'opérations gourmandes en mémoire.

Lorsque nous définissons la limite de mémoire à 512 Mo, elle déborde lorsque le RSS atteint environ 996 Mo.

Memory Usage (MB): {
  rss: '996.22 MB',
  heapTotal: '993.22 MB',
  heapUsed: '962.08 MB',
  external: '0.40 MB'
}
Memory Usage (MB): {
  rss: '996.23 MB',
  heapTotal: '993.22 MB',
  heapUsed: '962.09 MB',
  external: '0.40 MB'
}

<--- Last few GCs --->

[22540:0x7fd27684d000]     1680 ms: Mark-sweep 643.0 (674.4) -> 386.8 (419.4) MB, 172.2 / 0.0 ms  (average mu = 0.708, current mu = 0.668) allocation failure; scavenge might not succeed
[22540:0x7fd27684d000]     2448 ms: Mark-sweep 962.1 (993.2) -> 578.1 (610.7) MB, 240.7 / 0.0 ms  (average mu = 0.695, current mu = 0.687) allocation failure; scavenge might not succeed


<--- JS stacktrace --->

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
Copier après la connexion

En résumé, pour être plus précis, la limite de mémoire de Node.js fait référence à la limite de mémoire du tas, qui est la mémoire maximale pouvant être occupée par les objets JS, les tableaux, etc., alloués par la V8.

La taille de la mémoire du tas détermine-t-elle la quantité de mémoire qu'un processus Node.js peut occuper ? Non! Continuez à lire.

Puis-je mettre un fichier de 3 Go dans la mémoire Node.js ?

Nous avons vu lors du test que la baie ne peut contenir qu'un peu plus de 2 Go avant que le programme ne plante. Donc, si j'ai un fichier de 3 Go, ne puis-je pas le mettre d'un seul coup dans la mémoire Node.js ?

Vous pouvez !

Nous avons vu une mémoire externe via process.memoryUsage(), qui est occupée par le processus Node.js mais non allouée par la V8. Tant que vous y placez le fichier de 3 Go, il n'y a pas de limite de mémoire. Comment? Vous pouvez utiliser Buffer. Buffer est un module d'extension C de Node.js qui alloue de la mémoire à l'aide d'objets et de données C, et non JS.

Voici une démo :

setInterval(() => { console.log('Memory Usage:', process.memoryUsage()); }, 1000);
Copier après la connexion
Copier après la connexion

Même si vous allouez 3 Go de mémoire, notre programme fonctionne toujours correctement et notre programme Node.js a occupé plus de 5 Go de mémoire car cette mémoire externe n'est pas limitée par Node.js mais par la limite du système d'exploitation sur la mémoire allouée. aux threads (vous ne pouvez donc pas vous déchaîner, même Buffer peut manquer de mémoire ; l'essence est de gérer des données volumineuses avec Streams).

Dans Node.js, le cycle de vie d'un objet Buffer est lié à un objet JavaScript. Lorsque la référence JavaScript à un objet Buffer est supprimée, le garbage collector V8 marque l'objet comme recyclable, mais la mémoire sous-jacente de l'objet Buffer n'est pas immédiatement libérée. Généralement, lorsque le destructeur de l'extension C est appelé (par exemple, lors du processus de garbage collection dans Node.js), cette partie de la mémoire est libérée. Cependant, ce processus peut ne pas être complètement synchronisé avec le garbage collection de V8.

function formatMemoryUsageInMB(memUsage) {
    return {
        rss: convertToMB(memUsage.rss),
        heapTotal: convertToMB(memUsage.heapTotal),
        heapUsed: convertToMB(memUsage.heapUsed),
        external: convertToMB(memUsage.external)
    };
}

const convertToMB = value => {
    return (value / 1024 / 1024).toFixed(2) + ' MB';
};

const logInterval = setInterval(() => {
    const memoryUsageMB = formatMemoryUsageInMB(process.memoryUsage());
    console.log(`Memory Usage (MB):`, memoryUsageMB);
}, 1000);
Copier après la connexion
Copier après la connexion

En résumé : l'utilisation de la mémoire Node.js comprend l'utilisation de la mémoire du tas JS (déterminée par le garbage collection de V8) et l'allocation de mémoire par C

Pourquoi la mémoire tas est-elle séparée en nouvelles et anciennes générations ?

La stratégie générationnelle de garbage collection est très répandue dans les implémentations de langages de programmation modernes ! Des stratégies similaires telles que Generational Garbage Collection peuvent être trouvées dans Ruby, .NET et Java. Lorsque le garbage collection a lieu, cela conduit souvent à une situation « d'arrêt du monde », ce qui a inévitablement un impact sur les performances du programme. Cependant, cette conception est conçue dans un souci d'optimisation des performances.

  • Durée de vie des objets divergents Pendant le développement du programme, une partie importante des variables sont temporaires et servent à accomplir des tâches de calcul locales spécifiques. De telles variables conviennent mieux au GC mineur, c'est-à-dire au GC de nouvelle génération. Les objets de la mémoire nouvelle génération sont principalement soumis au garbage collection via l'algorithme Scavenge. L'algorithme Scavenge divise la mémoire du tas en deux parties, à savoir From et To (un compromis espace-temps classique. Grâce à leur courte durée de survie, ils ne consomment pas une grande quantité de mémoire).

Lorsque la mémoire est allouée, elle a lieu dans From. Lors du garbage collection, les objets actifs dans From sont inspectés et copiés vers To, suivis de la libération des objets non actifs. Lors du cycle de collecte suivant, les objets vivants de To sont répliqués vers From, auquel cas To se transforme en From et vice versa. À chaque cycle de garbage collection, From et To sont permutés. Cet algorithme réplique uniquement les objets vivants pendant le processus de copie et évite ainsi la génération de fragments de mémoire.
Alors, comment la vivacité d’une variable est-elle déterminée ? L’analyse d’accessibilité entre en jeu. Considérons les objets suivants à titre d'exemple :

  • globalObject : L'objet global.
  • obj1 : Un objet directement référencé par globalObject.
  • obj2 : Un objet référencé par obj1.
  • obj3 : un objet isolé sans aucune référence d'autres objets.

Dans le cadre de l'analyse d'accessibilité :

  • globalObject, étant un objet racine, est intrinsèquement accessible.
  • obj1, du fait qu'il est référencé par globalObject, est également accessible.
  • obj2, tel qu'il est référencé par obj1, est également accessible.
  • En revanche, obj3, dépourvu de chemins de référence vers l'objet racine ou d'autres objets accessibles, est jugé inaccessible et donc éligible au recyclage.

Certes, le comptage de références peut servir de moyen auxiliaire. Néanmoins, en présence de références circulaires, il ne parvient pas à déterminer avec précision la véritable vivacité des objets.

Dans la mémoire ancienne génération, les objets sont généralement moins actifs. Cependant, lorsque la mémoire ancienne génération devient pleine, cela déclenche le nettoyage de la mémoire ancienne génération (Major GC) via l'algorithme Mark-Sweep.

L'algorithme Mark-Sweep comprend deux phases : le marquage et le balayage. Lors de la phase de marquage, le moteur V8 parcourt tous les objets du tas et marque ceux qui sont actifs. Lors de la phase de balayage, seuls les objets non marqués sont effacés. Le mérite de cet algorithme est que la phase de balayage prend relativement moins de temps puisque la proportion d'objets morts dans l'ancienne génération est relativement faible. Cependant, son inconvénient est qu'il s'efface uniquement sans compacter, ce qui peut entraîner un espace mémoire discontinu, ce qui rend peu pratique l'allocation de mémoire pour des objets volumineux.

Cette lacune donne lieu à une fragmentation de la mémoire, nécessitant l'emploi d'un autre algorithme, Mark-Compact. Cet algorithme déplace tous les objets vivants vers une extrémité, puis éradique d'un seul coup l'espace mémoire invalide sur le côté droit de la limite, obtenant ainsi un espace mémoire disponible complet et continu. Il résout le problème de fragmentation de la mémoire qui pourrait être causé par l'algorithme Mark-Sweep, mais au prix de plus de temps nécessaire pour déplacer un grand nombre d'objets vivants.

Si vous trouvez cet article utile, merci de lui donner un coup de pouce. :D

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal