جاوا اسکریپت حافظه استاتیک با Object Pools

مقدمه

بنابراین ایمیلی دریافت می‌کنید که می‌گوید پس از مدت زمان معینی چگونه وب‌بازی/برنامه وب‌تان عملکرد بدی دارد، کد خود را بررسی می‌کنید، هیچ چیز برجسته‌ای را نمی‌بینید، تا زمانی که ابزارهای عملکرد حافظه Chrome را باز کنید، و اینو ببین:

یک عکس فوری از جدول زمانی حافظه شما

یکی از همکارانتان می‌خندد، زیرا متوجه می‌شوند که شما مشکل عملکردی مرتبط با حافظه دارید.

در نمای نمودار حافظه، این الگوی دندان اره بسیار گویای یک مشکل عملکرد بالقوه حیاتی است. همانطور که میزان استفاده از حافظه شما افزایش می‌یابد، می‌بینید که ناحیه نمودار نیز در ضبط خط زمانی رشد می‌کند. هنگامی که نمودار به طور ناگهانی کاهش می یابد، نمونه ای است که Garbage Collector اجرا شده است و اشیاء حافظه مرجع شما را تمیز می کند.

منظور از Saw-Tooths چیست

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

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

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

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

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

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

کاهش کاهش حافظه، کاهش مالیات جمع آوری زباله

همانطور که اشاره شد، یک پالس GC زمانی رخ می‌دهد که مجموعه‌ای از اکتشافات تعیین کنند که اشیاء غیرفعال کافی وجود دارد که یک پالس مفید باشد. به این ترتیب، کلید کاهش مدت زمانی که Garbage Collector از برنامه شما می گیرد، در حذف هرچه بیشتر موارد ایجاد و انتشار بیش از حد شی نهفته است. این فرآیند ایجاد/آزادسازی مکرر شیء را "تخلیه حافظه" می نامند. اگر بتوانید ریزش حافظه را در طول عمر برنامه خود کاهش دهید، مدت زمان اجرای GC را نیز کاهش می دهید. این بدان معنی است که شما باید تعداد اشیاء ایجاد شده و تخریب شده را حذف یا کاهش دهید، به طور موثر، باید تخصیص حافظه را متوقف کنید.

این فرآیند نمودار حافظه شما را از این قسمت منتقل می کند:

یک عکس فوری از جدول زمانی حافظه شما

به این:

جاوا اسکریپت حافظه استاتیک

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

حرکت به سمت جاوا اسکریپت با حافظه استاتیک

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

  1. برنامه خود را برای تعیین حداکثر تعداد اشیاء حافظه زنده مورد نیاز (در هر نوع) برای طیف وسیعی از سناریوهای استفاده ابزار کنید.
  2. کد خود را مجدداً برای تخصیص آن حداکثر مقدار، مجدداً پیاده‌سازی کنید و سپس به‌جای رفتن به حافظه اصلی، آنها را به‌صورت دستی واکشی/آزاد کنید.

در واقع، انجام شماره 1 مستلزم انجام کمی از شماره 2 است، بنابراین بیایید از آنجا شروع کنیم.

استخر آبجکت

به عبارت ساده، ادغام اشیاء فرآیند حفظ مجموعه ای از اشیاء استفاده نشده است که یک نوع مشترک دارند. هنگامی که به یک شی جدید برای کد خود نیاز دارید، به جای تخصیص یک شی جدید از سیستم Memory Heap ، در عوض یکی از اشیاء استفاده نشده را از استخر بازیافت می کنید. هنگامی که کد خارجی با شی انجام می شود، به جای رها کردن آن به حافظه اصلی، به pool بازگردانده می شود. از آنجا که شی هرگز از کد خارج نمی شود (معروف به حذف) زباله جمع آوری نمی شود. استفاده از استخرهای آبجکت کنترل حافظه را دوباره در دست برنامه نویس قرار می دهد و تأثیر جمع آوری زباله بر عملکرد را کاهش می دهد.

از آنجایی که یک مجموعه ناهمگن از انواع شی وجود دارد که یک برنامه آن را حفظ می کند، استفاده مناسب از اشیاء pool مستلزم آن است که برای هر نوع یک Pool داشته باشید که در طول زمان اجرا برنامه شما دچار ریزش زیاد شود.

var newEntity = gEntityObjectPool.allocate();
newEntity.pos = {x: 215, y: 88};

//..... do some stuff with the object that we need to do

gEntityObjectPool.free(newEntity); //free the object when we're done
newEntity = null; //free this object reference

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

پیش تخصیص اشیا

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

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

function init() {
  //preallocate all our pools. 
  //Note that we keep each pool homogeneous wrt object types
  gEntityObjectPool.preAllocate(256);
  gDomObjectPool.preAllocate(888);
}

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

به دور از گلوله نقره ای

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

نتیجه گیری

یکی از دلایلی که جاوا اسکریپت برای وب ایده آل است، به این واقعیت متکی است که زبانی سریع، سرگرم کننده و آسان برای شروع است. این عمدتاً به دلیل محدودیت کم آن برای محدودیت‌های نحوی و مدیریت مشکلات حافظه از طرف شما است. می توانید کد را حذف کنید و به آن اجازه دهید از کارهای کثیف مراقبت کند. با این حال، برای برنامه‌های کاربردی وب با کارایی بالا، مانند بازی‌های HTML5 ، GC اغلب می‌تواند نرخ فریم مورد نیاز را کاهش دهد و تجربه کاربر نهایی را کاهش دهد. با استفاده از ابزار دقیق و استفاده از استخرهای آبجکت، می‌توانید این بار را بر نرخ فریم خود کاهش دهید و آن زمان را برای چیزهای عالی‌تر پس بگیرید.

کد منبع

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

مراجع