در قسمت قبلی یاد گرفتیم چطور میتوان فهمید که پروسس اپمان مموری لیک دارد یا نه؟ خب حالا که مثل لوله کشان کاور پستمان فهمیدیم که پروسسمان مموری لیک دارد، چکار باید کنیم؟
خب، قبل از اینکه خودمان را درگیر دیباگر نود جی اس کنیم، ابتدا مواردی که ممکن است باعث مموری لیک اپلیکیشنمان شود را حدس بزنیم:
این توابع همان تابعهای سادهی خودمون هستند ولی با فرق اینکه یک سری نکات رو رعایت کنیم بهش میگن pure و در غیر اینصورت تابع impure نامیده میشود.
تابع خالص چیست:
تابع ناخالص چیست: توابعی که شروط بالا رو رعایت نکنند، ناخالصند :)
مثال تابع خالص:
function priceAfterTax(productPrice) { return (productPrice * 0.20) + productPrice; }
مثال تابع ناخالص:
var tax = 20; function calculateTax(productPrice) { return (productPrice * (tax/100)) + productPrice; }
برای اطلاعات بیشتر در مورد این نوع توابع این مقاله رو مطالعه کنید.
شاید دلیل اصلی بسیاری از توسعهدهندگانی که از نود استفاده میکنند همین تکنولوژی non-blocking باشه، که میتوان گفت یکی از شاهکارهای نود میباشد. اما خب برای توسعه دهندگانی که بهتازگی با نود آشنا شدند درک مفهوم آن ممکن است سخت باشد. از آنجا که نمیخوام این بحث را سختترش کنم با یک مثال ساده شرح میدم:
Blocking I/O
let data = db.users.find(very slow query) // stopped here by await OR yield console.log(data) console.log('im waiting for database and it is not good')
در این روش متد find تا زمانی که کوئری به صورت کامل اجرا نشده و خروجی را به متغیر data انتقال نداده بقیه کدها با اینکه به خروجی این کوئری احتیاجی ندارند بلاک شده و اجرا نخواهند شد
Non-Blocking I/O
db.users.find(very slow query, callback(data) { console.log(data) }) console.log(' i not waiting for database and it is awesome')
یکی از بهترین روشها برای بهره مندی از امکان non-blocking نودجیاس همین استفاده از توابع برگشتی و Promise ها است، که در این تکه کد به آن اشاره شد.
سعی کنید تا میتوانید از non-blocking ها استفاده کنید و با استفاده از توابع برگشتی و یا Promise ها کارایی اپ نودجیاستان رو بالا ببرید
اگر روشهای مختلفی را امتحان کردید اما مموری لیک پروسس شما از بین نرفت. پس تنها یک راه نهایی مانده تا بفهمید مشکل از کجاست. قبل از اینکه کارو شروع کنید ابتدا با چند اصطلاح آشنا بشیم.
یک ابزار عالی برای گرفتن اسنپ شات از پشته (heap) است، که بعدا میتوانید توسط اینسپکتور (inspector) گوگل کروم آن را آنالیز کنید.
این ابزار به شما اجازه میده که به پروسس نودجیاس در حال اجرا وصل شده و اسنپ شات هیپ گرفته و یا کدهای پروسستان را ویرایش کرده و دیباگ کنید ( بدون نیاز به ریستارت پروسس)
برای اینکه بتوانیم مموری لیک را لمس کنیم و نود اینسپکتور را در عمل مشاهده کنیم یک اپ کوچکی با فریم ورک اکسپرس نوشتم ولی با این تفاوت که این اپ کوچک دارای مموری لیک است!
memoryLeak.js:
const express = require('express') const app = express() const port = 6363 let globalArray = [];
app.get('/', (req, res) => {
globalArray.push(function thisIsTestFunc() { return req.headers; }) return res.send('hi, this is memory leak!') }) app.listen(port) console.log('process is running on port: ', port)
این تکه کد یک اپلیکیشن نودجیاس ساده هست که به ازای هر درخواست به آدرس '/' یک شی (Object) به آرایه گلوبال اضافه کرده و یک پاسخ به کاربر میدهد. مشکلی که وجود دارد این است که این آرایه گلوبال در مدت زمان کم (نسبت به تعداد درخواست) شروع به افزایش حجم مموری نموده و هیچ گاه خالی نمیشود، تا اینکه پروسس نودمان کرش کند!
برای اجرای پروسسمان در حالت دیباگر به این صورت اقدام میکنیم:
node --inspect memoryleak.js
بعد از اجرای پروسسمان، آدرس زیر را در مرورگر باز میکنیم:
http://localhost:9229/json/list
که در این صفحه یک خروجی JSON چاپ شده که شامل اطلاعات زیر است:
مقدار مشخصه devtoolsFrontendUrl را در مرورگر گوگل کروم باز میکنیم و وارد دیباگر نود اینسپکتور میشویم!
حتما از مرورگر گوگل کروم استفاده کنید زیرا این آدرس در مرورگرهای دیگر کار نخواهد کرد. برای امنیت بیشتر بعد از اینکه وارد دیباگر شدید، دیگر آدرس http://localhost:9229/json/list به شما خروجی جدید نخواهد داد!
به تب profile (در ورژن های جدید به نام memory تغییر یافته) دیباگر رفته و گزینه "allocation insurance on timeline" انتخاب کرده و سپس start را میزنیم
با استفاده از شبیه سازهای آماده تعدادی درخواست HTTP به سمت پروسس نودمان هدایت میکنیم، به محض اینکه درخواست ها به پروسس نودمان رسید میتوانیم تغییرات آبی رنگی را در نمودار افقی مشاهده کنیم بعد از گذشت 3 دقیقه تایم لاین را متوقف میکنیم و بازه های کوچکی را انتخاب کرده و مورد آنالیز قرار میدهیم. از آنجایی که این مموری لیک کاملا توسط خودمان ایجاد شده بود پس پیدا کردن آن بین این همه شی، آرایه و رشته کار چندان سختی نیست :) مستقیم به قسمت closure رفته و فاجعه را مشاهده میکنیم!!!
تعداد بسیار زیادی از تابع thisIsTestFunc تکرار شده است، اگر یادتان نباشد این تابع کارش این بود که شی req.headers رو به داخل آرایه گلوبال اضافه میکرد; اگر این اضافه کردنها به مدت بیشتری ادامه پیدا کند ممکن است رم بیشتری را اشغال کرده و در نهایت منجر به کرش اپ نودمان شود پس برای جلوگیری از این کار بعد از اینکه کارمان با آرایه گلوبال تمام شد مقدار آن را خالی میکنیم.
... app.get('/', (req, res) => { globalArray.push(function thisIsTestFunc() { return req.headers; }) // do some stuff globalArray = [] return res.send('hi, this is memory leak!') }) ...
این یک مثال بسیار ساده از مموری لیک بود، لزوما مموری لیک ها به این سادگی از بین نمیروند، نیاز به تحلیل و آنالیز بیشتر heapdump ها دارند، برای اینکه مموری لیک را بیابید سعی کنید در بازه های زمانی مختلف heapdump گرفته و آنها را باهم مقایسه کنید و یا در یک بازه چندین heapdump گرفته و آنها را مورد بررسی قرار دهید.
برای بیشتر یاد گرفتن، مقاله های زیر را پیشنهاد میکنم:
مفهوم GC ها در نودجیاس
برنامه نویسی Async در نودجیاس
جزئیات بیشتر کار با دیباگر و هیپ دامپ
از اینکه اپ نودجیاستان مموری لیک دارد خجالت نکشید; جستجو کنید، پشتکار داشته باشید و به تحلیل و بررسی ادامه دهید که تنها راه نابودی مموری لیک، وقت گرانبهای شماست!
اگر پیشنهادی، انتقادی و یا خطایی در صحبت هام بود ممنون میشم از قسمت کامنتها یا توییتر من رو در جریان بگذارید ♥