رفع مشکلات حافظه

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

خلاصه

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

بررسی اجمالی

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

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

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

نفخ حافظه: "خیلی زیاد" چقدر است؟

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

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

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

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

از Chrome Task Manager به عنوان نقطه شروع برای بررسی مشکل حافظه خود استفاده کنید. Task Manager یک مانیتور بیدرنگ است که به شما می گوید یک صفحه در حال حاضر از چه مقدار حافظه استفاده می کند.

  1. Shift+Esc را فشار دهید یا به منوی اصلی Chrome بروید و More tools > Task manager را انتخاب کنید تا Task Manager باز شود.

    باز کردن Task Manager

  2. روی هدر جدول Task Manager کلیک راست کرده و حافظه جاوا اسکریپت را فعال کنید.

    فعال کردن حافظه JS

این دو ستون به شما چیزهای مختلفی در مورد نحوه استفاده از حافظه صفحه شما می گویند:

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

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

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

  1. پنل Performance را در DevTools باز کنید.
  2. کادر انتخاب Memory را فعال کنید.
  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 در پنجره Overview (زیر NET ) هیپ JS را نشان می دهد. در زیر پنجره Overview ، پنجره Counter قرار دارد. در اینجا می توانید میزان استفاده از حافظه را به تفکیک JS Heap (همانند نمودار HEAP در پنجره Overview )، اسناد، گره های DOM، شنوندگان و حافظه GPU مشاهده کنید. غیرفعال کردن یک چک باکس آن را از نمودار پنهان می کند.

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

با Heap Snapshots، نشت حافظه درخت DOM جدا شده را کشف کنید

یک گره DOM فقط زمانی می تواند زباله جمع آوری شود که هیچ ارجاعی به آن از درخت DOM صفحه یا کد جاوا اسکریپت وجود نداشته باشد. گفته می شود که یک گره زمانی که از درخت DOM حذف می شود "جدا" می شود اما برخی جاوا اسکریپت هنوز به آن ارجاع می دهد. گره های DOM جدا شده یکی از دلایل شایع نشت حافظه هستند. این بخش به شما می آموزد که چگونه از Heap profilers 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 وجود ندارند، بنابراین جدا می شوند.

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

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

گرفتن عکس فوری پشته

پردازش و بارگیری عکس فوری ممکن است کمی طول بکشد. پس از اتمام، آن را از پانل سمت چپ (به نام HEAP SNAPSHOTS ) انتخاب کنید.

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

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

قیراط ها را گسترش دهید تا درخت جدا شده را بررسی کنید.

بررسی درخت جدا شده

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

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

بررسی یک گره زرد

نشت حافظه پشته JS را با Timelines تخصیص شناسایی کنید

Allocation Timeline ابزار دیگری است که می تواند به شما کمک کند نشت حافظه را در پشته JS خود ردیابی کنید.

برای نشان دادن جدول زمانی تخصیص کد زیر را در نظر بگیرید:

var x = [];

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

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

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

برای ضبط یک خط زمانی تخصیص، DevTools را باز کنید، به پانل Profiles بروید، دکمه رادیویی Record Allocation Timeline را انتخاب کنید، دکمه Start را فشار دهید، عملی را که گمان می‌کنید باعث نشت حافظه شده است را انجام دهید و سپس دکمه توقف ضبط را فشار دهید ( دکمه توقف ضبط ) وقتی کارتان تمام شد.

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

تخصیص های جدید

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

جدول زمانی تخصیص بزرگنمایی شده

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

جزئیات شی

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

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

نمایه تخصیص رکورد

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

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

نمایه تخصیص

جمع آوری زباله های مکرر را مشاهده کنید

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

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

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