برطرف کردن مشکلات حافظه

Kayce Basques
Kayce Basques

با نحوه استفاده از Chrome و DevTools برای پیدا کردن مشکلات حافظه که بر عملکرد صفحه تأثیر می‌گذارند آشنا شوید، ازجمله نشت حافظه، تورم حافظه، و جمع‌آوری مکرر زباله.

خلاصه

  • با «مدیر وظایف Chrome» ببینید صفحه‌تان چقدر حافظه مصرف می‌کند.
  • با ضبط‌های «خط زمان»، استفاده از حافظه را درطول زمان تجسم کنید.
  • درخت‌های DOM جداشده (علت رایج نشت حافظه) را با «تصاویر کلی پشته» شناسایی کنید.
  • با ضبط‌های «خط زمان تخصیص»، از زمان تخصیص حافظه جدید در پشته JS خود مطلع شوید.
  • عناصر جداشده‌ای را که براساس ارجاع جاوااسکریپت نگهداری می‌شوند، شناسایی کنید.

نمای کلی

در راستای مدل عملکرد RAIL، تمرکز تلاش‌های شما برای بهبود عملکرد باید روی کاربران باشد.

مشکلات حافظه مهم هستند زیرا اغلب توسط کاربران قابل‌تشخیص هستند. کاربران می‌توانند مشکلات حافظه را به روش‌های زیر درک کنند:

  • عملکرد صفحه به‌تدریج درطول زمان بدتر می‌شود. این احتمالاً نشانه‌ای از نشت حافظه است. نشت حافظه زمانی اتفاق می‌افتد که اشکالی در صفحه باعث می‌شود صفحه به‌تدریج درطول زمان از حافظه بیشتر و بیشتری استفاده کند.
  • عملکرد صفحه به‌طور مداوم بد است. این احتمالاً نشانه‌ای از تورم حافظه است. تورم حافظه زمانی اتفاق می‌افتد که صفحه از حافظه بیشتری نسبت به آنچه برای سرعت بهینه صفحه لازم است استفاده می‌کند.
  • عملکرد صفحه با تأخیر مواجه می‌شود یا به‌نظر می‌رسد مرتباً متوقف می‌شود. این احتمالاً نشانه‌ای از جمع‌آوری مکرر زباله است. جمع‌آوری زباله زمانی است که مرورگر حافظه را بازیابی می‌کند. مرورگر تصمیم می‌گیرد که این اتفاق چه زمانی رخ دهد. درطول جمع‌آوری، اجرای همه دستورات موقتاً متوقف می‌شود. بنابراین اگر مرورگر درحال جمع‌آوری زباله زیادی باشد، اجرای متن زیاد متوقف خواهد شد.

تورم حافظه: چقدر «بیش‌ازحد» است؟

تعریف نشت حافظه آسان است. اگر سایتی به‌طور فزاینده‌ای از حافظه بیشتر و بیشتری استفاده می‌کند، نشت حافظه دارید. اما تشخیص تورم حافظه کمی دشوارتر است. چه چیزی به‌عنوان «استفاده بیش‌ازحد از حافظه» درنظر گرفته می‌شود؟

هیچ عدد دقیقی در اینجا وجود ندارد، زیرا دستگاه‌ها و مرورگرهای مختلف قابلیت‌های متفاوتی دارند. همان صفحه‌ای که در تلفن هوشمند پیشرفته به‌راحتی اجرا می‌شود ممکن است در تلفن هوشمند ساده ازکار بیفتد.

نکته کلیدی در اینجا استفاده از مدل RAIL و تمرکز بر کاربران است. ببینید کدام دستگاه‌ها در بین کاربران شما محبوب هستند، سپس صفحه‌تان را در آن دستگاه‌ها آزمایش کنید. اگر تجربه به‌طور مداوم بد باشد، ممکن است صفحه از قابلیت‌های حافظه آن دستگاه‌ها فراتر رفته باشد.

نظارت بر مصرف حافظه به‌طور هم‌زمان با «مدیر وظایف Chrome»

از «مدیر فعالیت‌های Chrome» به‌عنوان نقطه شروعی برای بررسی مشکل حافظه استفاده کنید. «مدیر وظیفه» یک پایشگر هم‌زمان است که به شما می‌گوید یک صفحه چقدر حافظه مصرف می‌کند.

  1. کلیدهای Shift+Esc را فشار دهید یا به منو اصلی Chrome بروید و ابزارهای بیشتر > مدیر وظایف را انتخاب کنید تا «مدیر وظایف» باز شود.

    درحال باز کردن «مدیر وظایف».

  2. روی سرصفحه جدول «مدیر وظیفه» کلیک راست کنید و حافظه جاوا اسکریپت را فعال کنید.

    درحال فعال کردن حافظه JS در سرصفحه «مدیر وظایف».

این دو ستون اطلاعات متفاوتی درباره نحوه استفاده صفحه‌تان از حافظه ارائه می‌دهند:

  • ستون ردپای حافظه نشان‌دهنده حافظه سیستم‌عامل است. گره‌های DOM در حافظه سیستم‌عامل ذخیره می‌شوند. اگر این مقدار درحال افزایش است، گره‌های DOM درحال ایجاد شدن هستند.
  • ستون حافظه جاوااسکریپت نشان‌دهنده توده جاوااسکریپت است. این ستون دو مقدار دارد. مقداری که به آن علاقه دارید عدد زنده است (عدد داخل پرانتز). عدد زنده نشان‌دهنده میزان حافظه‌ای است که اشیای قابل‌دسترسی در صفحه‌تان استفاده می‌کنند. اگر این عدد درحال افزایش است، یا اشیای جدیدی درحال ایجاد شدن هستند یا اشیای موجود درحال رشد هستند.

    «مدیر وظایف» با سرصفحه حافظه جاوا اسکریپت فعال‌شده.

دیداری‌سازی نشت حافظه با ضبط‌های «عملکرد»

همچنین می‌توانید از پانل عملکرد به‌عنوان نقطه شروع دیگری در بررسی‌تان استفاده کنید. پانل «عملکرد» به شما کمک می‌کند مصرف حافظه صفحه را درطول زمان تجسم کنید.

  1. پانل عملکرد را در DevTools باز کنید.
  2. چارگوش انتخاب حافظه را فعال کنید.
  3. ضبط کنید.

برای نمایش ضبط‌های حافظه عملکرد، کد زیر را درنظر بگیرید:

var x = [];

function grow() {
  for (var i = 0; i < 10000; i++) {
    document.body.appendChild(document.createElement('div'));
  }
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

هر بار که دکمه ارجاع‌شده در کد فشار داده می‌شود، ده هزار گره div به بدنه سند اضافه می‌شود و رشته‌ای از یک میلیون نویسه x به آرایه x اضافه می‌شود. اجرای این کد، ضبط «خط زمان» را مانند نماگرفت زیر تولید می‌کند:

یک مثال ساده از رشد.

ابتدا، توضیحی درباره میانای کاربر. نمودار HEAP در قاب نمای کلی (زیر NET) نشان‌دهنده JS heap است. زیر قاب نمای کلی، قاب شمارنده قرار دارد. در اینجا می‌توانید استفاده از حافظه را به‌تفکیک توده JS (همانند نمودار HEAP در قاب نمای کلی)، اسناد، گره‌های DOM، شنوندگان، و حافظه GPU ببینید. غیرفعال کردن چارگوش انتخاب آن را از نمودار پنهان می‌کند.

اکنون، تحلیلی از کد در مقایسه با نماگرفت. اگر به شمارنده گره (نمودار سبز) نگاه کنید، می‌توانید ببینید که به‌طور واضح با کد مطابقت دارد. تعداد گره‌ها در مراحل مجزا افزایش می‌یابد. می‌توانید فرض کنید که هر افزایش در تعداد گره‌ها یک فراخوانی به grow() است. نمودار پشته JS (نمودار آبی) چندان ساده نیست. مطابق با روال‌های مطلوب، اولین افت درواقع یک جمع‌آوری زباله اجباری است (با فشار دادن دکمه جمع‌آوری زباله حاصل می‌شود). با پیشرفت ضبط، می‌توانید ببینید که اندازه پشته جاوااسکریپت افزایش می‌یابد. این طبیعی و قابل‌انتظار است: کد جاوا اسکریپت با هر کلیک روی دکمه، گره‌های DOM را ایجاد می‌کند و هنگام ایجاد رشته یک میلیون نویسه‌ای، کارهای زیادی انجام می‌دهد. نکته کلیدی در اینجا این است که پشته JS بالاتر از جایی که شروع شده است به پایان می‌رسد («شروع» در اینجا نقطه‌ای است که پس‌از جمع‌آوری اجباری زباله قرار دارد). در دنیای واقعی، اگر این الگوی افزایش اندازه پشته JS یا اندازه گره را ببینید، احتمالاً به‌معنای نشت حافظه است.

کشف کردن نشت حافظه درخت DOM جداشده با «تصویر کلی پشته»

گره DOM فقط زمانی می‌تواند جمع‌آوری زباله شود که هیچ ارجاعی به آن از درخت DOM صفحه یا کد جاوا اسکریپت وجود نداشته باشد. وقتی گره‌ای از درخت DOM برداشته شود اما همچنان برخی‌از جاوا اسکریپت‌ها به آن ارجاع دهند، گفته می‌شود که آن گره «جداشده» است. گره‌های جداشده DOM یکی از دلایل رایج نشت حافظه هستند. این بخش به شما آموزش می‌دهد که چگونه از تحلیل‌گرهای پشته DevTools برای شناسایی گره‌های جداشده استفاده کنید.

در اینجا نمونه ساده‌ای از گره‌های DOM جداشده آورده شده است.

var detachedTree;

function create() {
  var ul = document.createElement('ul');
  for (var i = 0; i < 10; i++) {
    var li = document.createElement('li');
    ul.appendChild(li);
  }
  detachedTree = ul;
}

document.getElementById('create').addEventListener('click', create);

با کلیک کردن روی دکمه ارجاع‌شده در کد، یک گره ul با ده فرزند li ایجاد می‌شود. این گره‌ها توسط کد ارجاع داده می‌شوند اما در درخت DOM وجود ندارند، بنابراین جدا شده‌اند.

تصاویر کلی پشته یکی از راه‌های شناسایی گره‌های جداشده است. همان‌طور که از نام آن پیداست، تصویر کلی پشته به شما نشان می‌دهد که حافظه چگونه بین اشیای جاوا اسکریپت و گره‌های DOM صفحه شما در زمان گرفتن تصویر کلی توزیع شده است.

برای ایجاد عکس آنی، DevTools را باز کنید و به پانل حافظه بروید، دکمه رادیویی عکس آنی پشته را انتخاب کنید، و سپس دکمه گرفتن عکس آنی را فشار دهید.

دکمه رادیویی «گرفتن تصویر کلی پشته» انتخاب شده است.

ممکن است پردازش و بار کردن عکس آنی کمی طول بکشد. وقتی تمام شد، آن را از پانل سمت چپ (به‌نام Heap snapshots) انتخاب کنید.

برای جستجوی درخت‌های DOM جداشده، Detached را در چارگوش ورودی فیلتر کلاس تایپ کنید.

درحال فیلتر کردن گره‌های جداشده.

برای بررسی درخت جداشده، علامت‌های هشتک را ازهم باز کنید.

درحال بررسی درخت جداشده.

برای بررسی بیشتر گره، روی آن کلیک کنید. در قاب اشیاء می‌توانید اطلاعات بیشتری درباره کدی که به آن ارجاع می‌دهد ببینید. برای مثال، در نماگرفت زیر می‌توانید ببینید که متغیر detachedTree به گره ارجاع می‌دهد. برای رفع این نشت حافظه خاص، کدی را که از detachedTree استفاده می‌کند بررسی می‌کنید و مطمئن می‌شوید که وقتی دیگر به آن نیاز نیست، ارجاعش به گره را برمی‌دارد.

درحال بررسی گره جداشده.

شناسایی نشت حافظه پشته جاوااسکریپت با «خطوط زمان تخصیص»

خط زمان تخصیص ابزار دیگری است که می‌تواند به شما کمک کند نشت حافظه را در توده JS خود ردیابی کنید.

برای نشان دادن خط زمان تخصیص، کد زیر را درنظر بگیرید:

var x = [];

function grow() {
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

هر بار که دکمه ارجاع‌شده در کد فشار داده می‌شود، رشته‌ای از یک میلیون نویسه به آرایه x اضافه می‌شود.

برای ضبط کردن خط زمان تخصیص، DevTools را باز کنید، به پانل حافظه بروید، دکمه رادیویی تخصیص‌ها در خط زمان را انتخاب کنید، دکمه ضبط را فشار دهید، کنشی را که فکر می‌کنید باعث نشت حافظه می‌شود انجام دهید، و سپس وقتی کارتان تمام شد دکمه توقف ضبط را فشار دهید.

درحین ضبط، ببینید آیا نوار آبی‌رنگی در خط زمان تخصیص نشان داده می‌شود یا نه، مانند نماگرفت زیر.

تخصیص‌های جدید در خط زمان عملکرد.

آن نوارهای آبی نشان‌دهنده تخصیص‌های حافظه جدید هستند. این تخصیص‌های حافظه جدید نامزدهای شما برای نشت حافظه هستند. می‌توانید روی یک نوار زوم کنید تا قاب سازنده را فیلتر کنید تا فقط اشیایی را نشان دهد که درطول بازه زمانی مشخص‌شده اختصاص داده شده‌اند.

خط زمان تخصیص بزرگ‌شده.

شیء را ازهم باز کنید و روی مقدار آن کلیک کنید تا جزئیات بیشتری درباره آن در قاب شیء مشاهده کنید. برای مثال، در نماگرفت زیر، با مشاهده جزئیات شیء جدیداً اختصاص‌داده‌شده، می‌توانید ببینید که به متغیر x در محدوده Window اختصاص داده شده است.

جزئیات شیء آرایه رشته‌ای.

بررسی تخصیص حافظه براساس تابع

از نوع نمایه نمونه‌گیری تخصیص در پانل حافظه برای مشاهده تخصیص حافظه براساس تابع جاوا اسکریپت استفاده کنید.

نمایه‌ساز نمونه‌گیری تخصیص در پانل «حافظه».

  1. دکمه رادیویی نمونه‌گیری تخصیص را انتخاب کنید. اگر کارگری در صفحه وجود دارد، می‌توانید آن را به‌عنوان هدف نمایه‌سازی از پنجره انتخاب نمونه ماشین مجازی جاوا اسکریپت انتخاب کنید.
  2. دکمه شروع را فشار دهید.
  3. کنش‌های موردنظرتان را برای بررسی در صفحه انجام دهید.
  4. وقتی همه کنش‌هایتان را انجام دادید، دکمه توقف را فشار دهید.

DevTools تفکیک تخصیص حافظه را براساس تابع به شما نشان می‌دهد. نمای پیش‌فرض سنگین (پایین به بالا) است که توابعی را که بیشترین حافظه را اختصاص داده‌اند در بالا نمایش می‌دهد.

صفحه نتایج نمایه تخصیص.

شناسایی اشیای حفظ‌شده توسط ارجاع جاوااسکریپت

نمایه عناصر جداشده عناصر جداشده‌ای را به شما نشان می‌دهد که به‌دلیل ارجاع داده شدن توسط کد جاوااسکریپت باقی می‌مانند.

نمایه عناصر جداشده را ضبط کنید تا گره‌های HTML و تعداد گره‌ها را دقیقاً مشاهده کنید.

نمونه نمایه عناصر جداشده.

زباله‌روبی‌های مکرر را شناسایی کنید

اگر به‌نظر می‌رسد صفحه‌تان مکرراً متوقف می‌شود، ممکن است مشکل جمع‌آوری زباله داشته باشید.

برای شناسایی جمع‌آوری‌های مکرر زباله می‌توانید از «مدیر وظیفه Chrome» یا ضبط‌های حافظه «خط زمان» استفاده کنید. در «مدیر وظیفه»، مقادیر حافظه یا حافظه جاوا اسکریپت که به‌طور مکرر افزایش و کاهش می‌یابند نشان‌دهنده جمع‌آوری مکرر زباله هستند. در ضبط‌های «خط زمان»، نمودارهای تعداد گره یا توده JS که مکرراً بالا و پایین می‌روند نشان‌دهنده جمع‌آوری مکرر زباله است.

پس‌از شناسایی مشکل، می‌توانید از ضبط «خط زمان تخصیص» برای پیدا کردن محل تخصیص حافظه و توابعی که باعث تخصیص می‌شوند استفاده کنید.