برنامه نویسی

آیا برنامه وب شما در حال نشت است؟ راهنمای مبتدی برای یافتن و رفع مشکلات حافظه (قسمت 1)

تا به حال متوجه شده اید که برنامه وب شما با گذشت زمان لاغر می شود؟ یا شاید گاهی اوقات به نظر نمی رسد که به ظاهر خراب شود؟ ممکن است یک نشت حافظه روی دستان خود داشته باشید!

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

اما نگران نباشید! تشخیص و رفع این نشت ها برخی از هنرهای طاقت فرسا نیست. در این بخش اول راهنمای ما (با الهام از فیلم عالی Metenski در جبهه رمزگشایی شده.) ، ما اصول اولیه نشت حافظه را در برنامه های وب بررسی خواهیم کرد و شما را با ابزارهای قدرتمند در زرادخانه مرورگر خود آرسنال آشنا می کنیم.

نشت حافظه دقیقاً چیست؟

نشت حافظه زمانی اتفاق می افتد که برنامه شما فراموش کند که برخی از این اطلاعات را پاک کند. نگه داشتن آن را نگه می دارد ، حتی اگر دیگر مورد استفاده قرار نمی گیرد. تصور کنید که یادداشت های قدیمی را در سراسر تخته سفید بگذارید – در نهایت ، شما از فضا فرار خواهید کرد!

در JavaScript ، زبانی که بیشتر برنامه های وب را تأمین می کند ، یک سیستم هوشمندانه به نام “جمع آوری زباله” وجود دارد که به طور خودکار سعی می کند این حافظه بلااستفاده را تمیز کند. با این حال ، نشت هنگامی اتفاق می افتد که اتصالات ناخواسته یا “منابع” مانع از جمع آوری زباله ها از انجام کار خود شود.

مقصر مشترک: متغیرهای جهانی تصادفی:

اگر فراموش کرده اید متغیر را با اجازه دهید ، const یا var در داخل یک عملکرد ، می تواند به طور تصادفی به یک متغیر جهانی تبدیل شود. متغیرهای جهانی برای کل طول عمر برنامه شما ، که به طور بالقوه روی داده های غیر ضروری نگه داشته می شوند ، می چسبند.

function doSomething() {
  message = "Hello!"; // Oops! 'message' becomes a global variable
  console.log(message);
}
doSomething();
console.log(message); // Still accessible globally!
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

تایمر و فواصل فراموش شده:

اگر تنظیم کنید setTimeout یا setInterval عملکرد برای اجرای کدی به طور مکرر ، و آن را با آن پاک نمی کنید ClearTimeout یا ClearInterval هنگامی که شما با آن تمام شد ، این کارکردها می توانند برنامه شما را زنده نگه دارند و از جمع آوری زباله های مربوط به داده های مرتبط جلوگیری کنند.

let intervalId = setInterval(() => {
  console.log("This keeps running...");
}, 1000);

// If we don't do this:
// clearInterval(intervalId);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

شنوندگان رویداد آویزان:

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

const button = document.getElementById('myButton');
function handleClick() {
  console.log('Button clicked!');
}
button.addEventListener('click', handleClick);

// Later, if the button is removed from the DOM:
// button.remove();
// The event listener might still be in memory if not explicitly removed:
// button.removeEventListener('click', handleClick);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

بسته شدن بیش از حد:

بسته شدن در JavaScript قدرتمند است ، اما اگر دامنه درونی یک عملکرد (“بسته شدن” آن) حتی پس از اتمام عملکرد بیرونی ، دسترسی به مقادیر زیادی از داده ها را حفظ کند ، می تواند منجر به ایجاد حافظه شود.

function outerFunction(largeData) {
  return function innerFunction() {
    console.log('Data is still here:', largeData.length);
    // Even after outerFunction finishes, innerFunction can still access largeData
  };
}

const bigData = new Array(1000000).fill('*');
const closure = outerFunction(bigData);
// Even if we don't call closure immediately, it 'remembers' bigData
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

انواع مختلفی از نشت نیز وجود دارد. برخی از آنها به نام “نشت استاتیک” ممکن است اندازه ثابت داشته باشند. اما نگران کننده تر “نشت پویا” است که با گذشت زمان با زمان تعامل با برنامه رشد می کند و در نهایت منجر به مشکلات قابل توجه عملکرد یا خرابی می شود.

تبدیل شدن به یک کارآگاه نشت حافظه: ابزار اشکال زدایی شما

خبر خوب این است که مرورگرهای وب مدرن مجهز به ابزارهای توسعه دهنده فوق العاده هستند تا به شما در ردیابی این Gremlins حافظه کمک کنند. ما در این راهنما روی Devtools Google Chrome تمرکز خواهیم کرد.

قبل از شیرجه زدن به ابزارها ، در اینجا چند روش خوب برای به خاطر سپردن وجود دارد:

از حالت ناشناس استفاده کنید: این یک محیط آزمایش تمیز و بدون برنامه افزودنی مرورگر را تضمین می کند.
ساخت تولید خود را آزمایش کنید: بعضی اوقات ، بهینه سازی در توسعه شما می تواند مسائل مربوط به حافظه را که فقط در نسخه نهایی و تولید ظاهر می شود ، نقاب کند.
برای کاربران زاویه ای (و سایر چارچوب ها با فرآیندهای ساخت): در طی مراحل ساخت خود به طور موقت غیرفعال کردن نام متغیر را در نظر بگیرید. این می تواند درک نام اشیاء در Devtools را بسیار ساده تر کند.
حال ، بیایید به زبانه های ضروری Devtools برای شکار در حافظه نگاه کنیم:

1. برگه عملکرد: گرفتن تصویر بزرگ

برگه عملکرد جدول زمانی از فعالیت برنامه خود را به شما می دهد. هنگام بررسی حافظه ، به دنبال “js heap” نمودار (معمولاً یک خط آبی). این نمودار نشان می دهد که کد JavaScript شما با گذشت زمان چقدر حافظه می کند.

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

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

2. برگه حافظه: غواصی به جزئیات

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

عکسهای فوری: تصور کنید که از همه اشیاء و داده هایی که در حال حاضر در حافظه برنامه خود ساکن هستند ، یک تصویر فریز را بگیرید. عکسهای فوری به شما امکان می دهد همین کار را انجام دهید. شما می توانید چندین عکس فوری را در نقاط مختلف چرخه عمر برنامه خود بگیرید و سپس آنها را با یکدیگر مقایسه کنید تا ببینید چه اشیاء ایجاد می شوند و به طور مهم ، چه اشیاء تمیز نمی شوند. با مقایسه عکسهای فوری قبل و بعد از انجام یک عمل خاص در برنامه خود ، می توانید اشیاء را که به طور غیر منتظره جمع می شوند ، شناسایی کنید. به دنبال یک “دلتا” مثبت باشید – اشیاء بیشتری ایجاد شده از حذف شده. الگوهای مداوم در چندین آزمایش می تواند ظن شما را به نشت تقویت کند. همچنین می توانید “نگهدارنده” یک شی را بازرسی کنید تا ببینید چه اشیاء دیگری روی آن نگه داشته می شوند و از جمع آوری زباله جلوگیری می کنند.

تخصیص در جدول زمانی: این ابزار هنگام تعامل با برنامه خود ، تخصیص حافظه را در زمان واقعی ردیابی می کند. هنگامی که اقداماتی مانند پیمایش بین صفحات یا تعامل با مؤلفه ها انجام می دهید ، سنبله هایی را در استفاده از حافظه مشاهده خواهید کرد. این می تواند به شما کمک کند تا مشخص کنید که اقدامات منجر به اختصاص حافظه می شود. مهمتر از همه ، می بینید که آیا حافظه اختصاص یافته پس از آن آزاد می شود یا خیر. به دنبال اشیاء باشید که یک “اندازه اختصاص یافته” به طور مداوم در حال رشد را نشان می دهد – این می تواند نشانه ای از انتشار صحیح آن اشیاء باشد.

با استفاده از این ابزارها و مشاهده دقیق نحوه رفتار حافظه برنامه ، می توانید آن نشت حافظه دزدکی را شناسایی کنید.

یک مثال در دنیای واقعی: مورد اشتراک طولانی

ویدیوی Metenski نمونه ای عالی از نشت حافظه مشترک در کاربردهای زاویه ای را ارائه می دهد: اشتراک RXJS که هنگام از بین رفتن یک مؤلفه به درستی اشتراک نمی کند. این بدان معنی است که حتی بعد از اینکه مؤلفه دیگر روی صفحه قابل مشاهده نباشد ، اشتراک هنوز در پس زمینه فعال است و روی نمونه مؤلفه نگه داشته شده و از جمع آوری زباله جلوگیری می کند.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { interval, Subscription } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';

@Component({
  selector: 'app-leaky',
  template: `

Leaky Component

`, }) export class LeakyComponent implements OnInit, OnDestroy { private intervalSubscription: Subscription | undefined; private destroy$ = new Subject(); ngOnInit(): void { this.intervalSubscription = interval(1000) .pipe(takeUntil(this.destroy$)) // Prevents leak .subscribe(() => { console.log('Tick...'); }); } ngOnDestroy(): void { // Without this, the subscription would likely persist, causing a memory leak if (this.intervalSubscription) { this.intervalSubscription.unsubscribe(); } this.destroy$.next(); this.destroy$.complete(); } } @Component({ selector: 'app-fixed', template: `

Fixed Component

`, }) export class FixedComponent implements OnInit, OnDestroy { private intervalSubscription: Subscription | undefined; ngOnInit(): void { this.intervalSubscription = interval(1000).subscribe(() => { console.log('Tick (fixed)...'); }); } ngOnDestroy(): void { if (this.intervalSubscription) { this.intervalSubscription.unsubscribe(); } } } // Newer Angular versions often recommend using takeUntilDestroyed: // import { takeUntilDestroyed } from '@angular/core/rxjs'; // ... // ngOnInit(): void { // interval(1000) // .pipe(takeUntilDestroyed(this.destroyRef)) // .subscribe(() => { // console.log('Tick (modern Angular)...'); // }); // }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

راه حل؟ استفاده از اپراتورها دارای یابندر (موجود در نسخه های جدیدتر از زاویه ای) یا به صورت دستی اشتراک در مؤلفه ها ناله HOOK چرخه عمر تضمین می کند که در صورت عدم نیاز به مؤلفه ، اشتراک آن تمیز می شود و از نشت حافظه جلوگیری می کند.

چه چیزی بعدی؟

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

بنابراین ، یک چشم برای قسمت 2 ، و در ضمن ، شروع به آزمایش با زبانه های عملکرد و حافظه در Devtools مرورگر خود کنید. اشکال زدایی مبارک!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا