Maison > Article > interface Web > Explication détaillée du problème de fuite de mémoire nodeJs
J'ai découvert par hasard avant que lors du rendu de React sur le serveur, lorsque NODE_ENV != production, cela provoquerait une fuite de mémoire. Problèmes spécifiques : https://github.com/facebook/react/issues/7406. Avec l'utilisation généralisée de l'isomorphisme des nœuds et des réactions et d'autres technologies, des problèmes tels que les fuites de mémoire côté nœud devraient attirer notre attention. Pourquoi le nœud est sujet aux fuites de mémoire et comment les résoudre une fois qu'elles se produisent. Vous trouverez ci-dessous une brève introduction et un exemple pour illustrer.
Tout d'abord, node est basé sur le moteur v8, et sa méthode de gestion de la mémoire est cohérente avec la v8. Ce qui suit est une brève introduction aux effets de mémoire associés de la v8.
Limite de mémoire V8
le nœud est construit sur la base de V8 et alloue et gère les objets js via V8. V8 a des restrictions sur l'utilisation de la mémoire (la mémoire d'ancienne génération est d'environ 1,4 Go dans les systèmes 64 bits, environ 0,7 Go dans les systèmes 32 bits, la mémoire de nouvelle génération dans les systèmes 64 bits est d'environ 32 Mo et les systèmes 32 bits sont d'environ 16 Mo). Avec de telles restrictions, les objets mémoire volumineux ne peuvent pas être manipulés. Si cette limite est accidentellement touchée, le processus se terminera.
Raison : V8 bloquera la logique d'application JavaScript lors de l'exécution du garbage collection, puis réexécutera la logique d'application JavaScript jusqu'à ce que le garbage collection soit terminé. Ce comportement est appelé "arrêter le monde". Si la mémoire tas du V8 est de 1,5 Go, il faut plus de 50 ms au V8 pour effectuer un petit garbage collection, et même plus d'une seconde pour effectuer un garbage collection non incrémentiel.
Utilisez node --max-old-space-size=xxx (unité Mo), node --max-new-space-size=xxx (unité Ko) pour définir la mémoire de nouvelle génération et la mémoire d'ancienne génération pour casser la limite de mémoire par défaut.
La composition du tas du V8
Le tas du V8 n'est en fait pas seulement composé de l'ancienne génération et de la nouvelle génération. Le tas peut être divisé en plusieurs zones différentes :
Zone mémoire nouvelle génération : La plupart des objets sont alloués ici. Cette zone est petite mais le garbage collection est très fréquent Zone de pointeur d'ancienne génération : Elle appartient à l'ancienne génération. Elle contient la plupart des pointeurs pouvant pointer vers. autres objets. Objets, la plupart des objets promus de la nouvelle génération seront déplacés iciZone de données d'ancienne génération : appartient à l'ancienne génération, seuls les objets de données d'origine sont enregistrés ici, ces objets n'ont pas de pointeurs vers d'autres objets. Grande zone d'objets : c'est là que sont stockés les objets dont la taille dépasse la taille des autres zones. Chaque objet a sa propre mémoire. Les objets volumineux ne seront pas déplacés lors du garbage collection. : Objets de code, qui contiennent des instructions après JIT. L'objet sera alloué ici. La seule zone mémoire avec des autorisations d'exécutionZone de cellule, zone de cellule d'attribut, zone de carte : stocke la cellule, la cellule d'attribut et la carte. Chaque zone stocke des éléments de même taille et a une structure simple.Type de collecte GC
Analyse des fuites de mémoire
process.memoryUsage(); { ress: 47038464, heapTotal: 34264656, heapUsed: 2052866 }ress : la partie mémoire résidente du processus heapTotal, tasUtilisé : informations sur la mémoire du tas V8 Vérifiez l'utilisation de la mémoire système (octet unitaire)os.totalmem()
os.freemem()
Renvoyer la mémoire système totale et la mémoire inactive
Outils d'analyse et de surveillance
node-heapdump capture des instantanés de la mémoire du tas v8
analyses node -mtrace la pile en utilisantnode-memwatch pour surveiller le garbage collection
node-memwatch
memwatch.on('stats',function(info){ console.log(info) }) memwatch.on('leak',function(info){ console.log(info) })événement de statistiques : Chaque Chaque fois qu'un garbage collection complet du tas est effectué, un événement stats sera déclenché. Cet événement fournira des statistiques de mémoire.
{ "num_full_gc": 17, //第几次全栈垃圾回收 "num_inc_gc": 8, //第几次增量垃圾回收 "heap_compactions": 8, //第几次对老生代进行整理 "estimated_base": 2592568, //预估基数 "current_base": 2592568, //当前基数 "min": 2499912, //最小 "max": 2592568, //最大 "usage_trend": 0 //使用趋势 }Observez num_full_gc et num_inc_gc pour refléter la situation du ramassage des ordures. Événement de fuite : Si la mémoire n'est toujours pas libérée après 5 garbage collection consécutives, cela signifie qu'une fuite de mémoire se produit. A ce moment, un événement de fuite sera déclenché.
{ start: Fri, 29 Jun 2012 14:12:13 GMT, end: Fri, 29 Jun 2012 14:12:33 GMT, growth: 67984, reason: 'heap growth over 5 consecutive GCs (20s) - 11.67 mb/hr' }
Heap Diffing 堆内存比较 排查内存溢出代码。
下面,我们通过一个例子来演示如何排查定位内存泄漏:
首先我们创建一个导致内存泄漏的例子:
//app.js var app = require('express')(); var http = require('http').Server(app); var heapdump = require('heapdump'); var leakobjs = []; function LeakClass(){ this.x = 1; } app.get('/', function(req, res){ console.log('get /'); for(var i = 0; i < 1000; i++){ leakobjs.push(new LeakClass()); } res.send('<h1>Hello world</h1>'); }); setInterval(function(){ heapdump.writeSnapshot('./' + Date.now() + '.heapsnapshot'); }, 3000); http.listen(3000, function(){ console.log('listening on port 3000'); });
这里我们通过设置一个不断增加且不回被回收的数组,来模拟内存泄漏。
通过使用heap-dump模块来定时纪录内存快照,并通过chrome开发者工具profiles来导入快照,对比分析。
我们可以看到,在浏览器访问 localhost:3000 ,并多次刷新后,快照的大小一直在增长,且即使不请求,也没有减小,说明已经发生了泄漏。
接着我们通过过chrome开发者工具profiles, 导入快照。通过设置comparison,对比初始快照,发送请求,平稳,再发送请求这3个阶段的内存快照。可以发现右侧new中LeakClass一直增加。在delta中始终为正数,说明并没有被回收。
小结
针对内存泄漏可以采用植入memwatch,或者定时上报process.memoryUsage内存使用率到monitor,并设置告警阀值进行监控。
当发现内存泄漏问题时,若允许情况下,可以在本地运行node-heapdump,使用定时生成内存快照。并把快照通过chrome Profiles分析泄漏原因。若无法本地调试,在测试服务器上使用v8-profiler输出内存快照比较分析json(需要代码侵入)。
需要考虑在什么情况下开启memwatch/heapdump。考虑heapdump的频度以免耗尽了CPU。 也可以考虑其他的方式来检测内存的增长,比如直接监控process.memoryUsage()。
当心误判,短暂的内存使用峰值表现得很像是内存泄漏。如果你的app突然要占用大量的CPU和内存,处理时间可能会跨越数个垃圾回收周期,那样的话memwatch很有可能将之误判为内存泄漏。但是,这种情况下,一旦你的app使用完这些资源,内存消耗就会降回正常的水平。所以需要注意的是持续报告的内存泄漏,而可以忽略一两次突发的警报。
更多Explication détaillée du problème de fuite de mémoire nodeJs相关文章请关注PHP中文网!