navidkhm
navidkhm
خواندن ۶ دقیقه·۲ سال پیش

حافظه و نحوه ذخیره‌سازی داده‌ها + انواع داده در JS - بخش ۱

برای همه‌مون دیدن ارور زیر پیش اومده :

ارور Maximum call stack size exceeded
ارور Maximum call stack size exceeded

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

حالا یکم قراره جزئی‌تر بدونیم و ببینیم وقتی کدی رو می‌نویسیم متغیرهامون دقیقا چجوری ذخیره میشن :

Stack and Heap Memory:

ما داخل حافظه (اینجا داریم در مورد RAM حرف میزنیم نه Disk Drive) دو بخش داریم.

یکی Stack و اون یکی Heap و اگه ساختمان داده یادتون باشه یه بحث بامزه‌ای این وسط هست که آیا واقعا این دوتا بخش تو حافظه همون دو نوعِ ساختمان داده هستن؟؟ که اینجا بهش نمی‌پردازیم و صرفا همون دو بخشِ مختلف از حافظه رو که توش داده ذخیره میشه میگیم استک و هیپ؛ دقت کنید جفتشون توی RAM هستن.

استک و هیپ دو بخش مختلف از حافظه (فقط شکل گولتون نزنه مقدار استک خیلیییی کمتر از هیپه)
استک و هیپ دو بخش مختلف از حافظه (فقط شکل گولتون نزنه مقدار استک خیلیییی کمتر از هیپه)


نحوه ذخیره‌سازی داده در استک و هیپ

استک جاییه که توش میتونیم مقدار تعیین‌شده‌ای از داده رو پشت سر هم ذخیره کنیم یعنی :

تو تصویر بالا ما یه متغیر نوشتیم و مقدارش رو برابر یه عدد گذاشتیم و مقداری هم که عدد تو حافظه جا میگیره ۸ بایته(تقریبا) و زبون ما اینو میدونه پس میگه آقا متغیر (a) ۸ بایت جا میخواد پس میذارمش تو استک و استک هم ۸ بایت از حافظه‌ای که داره رو پر میکنه و پوینتر ۸ واحد جلوتر میره و میتونه متغیر بعدی رو ذخیره کنه و چون دقیقا میدونه چقدر فضا باید به داده‌ی ما بده خیلی سریع اینکارو انجام میده؛ به این حالت میگیم Static Memory Allocation (تخصیص حافظه‌ به شکل ثابت). از اونطرف هم هیپ رو داریم که متغیری که داخلش ذخیره می‌کنیم سایز مشخصی نداره و میتونه بزرگ و کوچیک شه یاااا اینکه مقدار متغیرم بزرگه و خیلی راحت‌تره که توی هیپ انداخته بشه بخاطر اینکه گفتیم مقداری که استک از سیستم‌عامل (OS) میگیره خیلی محدوده (در حد 1Mb یا تهش چند مگ)*.
حالا فرض کنید که یه آبجکت دارم که چندتا پراپرتی داره و قراره بره توی هیپ ذخیره بشه، اگه بخوام خیلی مختصر بگم زبونمون اول میره میپرسه آقا کجاها از حافظه خالیه؟ لیستشو میگیره و بعد میره متغیرمون رو تو یکی از اون فضاها ذخیره میکنه و اون آدرسی از حافظه رو که توش متغیرمون رو ریخته (که طولش رو میدونیم چون یه عدده :دی) تو استک ذخیره میکنه و وقتی ما میخوایم آبجکتمون رو فراخوانی کنیم برنامه میره روی اون بخش از استک و میبینه یه آدرس از حافظه اونجا ذخیره شده و میره اون آدرس رو میخونه و مقداری که میخوایم رو برگردونه.
یه نکته هم ‌که گفتم اینه که هیپ میتونه زیاد بشه یعنی بنا به نیاز برنامه طولش رو زیاد کنه و مثل استک محدودیت نداره**

شماتیک داده‌ی ذخیره‌شده در استک و هیپ
شماتیک داده‌ی ذخیره‌شده در استک و هیپ

انواع داده در JS

تا اینجا فهیمدیم که دو مدل فضا تو RAM داریم و چجوری کار میکنن ولی وقتی ما یه سرچ تو اینترنت میزنیم در مورد انواع ذخیره‌سازی داده‌ تو JS میگن دو مدل داده (Data Type) داریم:

1.Primitive Type (نوع اولیه)***

2.Reference Type (نوع ارجاعی)

نوع اولیه اونایی‌ن که خودِ مقدارشون مستقیم توی استک قرار میگیره و ارجاعی‌ها(Reference Type) هم از اسمشون معلومه آدرس حافظه‌شون(پوینتر در واقع) تو استک قرار میگیره که تو شکل بالا هم نشون دادیم.

بعد هم میگن آبجکت و تابع و آرایه(۴*) از نوع ارجاعی هستن پس میرن توی هیپ و مابقی انواع داده میرن توی استک ذخیره می‌شن :))) و خب واقعا این چه طرز توضیحه!!!

این حرف تا حدی مسئله‌سازه چرا که عملا ما دیگه فکر میکنیم همه‌جا اینطوریه و کلا هر جا آرایه‌ دیدیم پس میفهمیم که مقدارش توی هیپ ذخیره میشه.اما جای قشنگش اینه که تو زبونای سطح پایین مثل C و ++C ما میتونیم تعیین کنیم که کدوم متغیر توی هیپ بره مثلا من یه متغیر عددی تعریف میکنم ولی میگم تو برو و مقدارتو توی هیپ ذخیره کن مثل کد زیر:

تعریف یک متغیرِ ارجاعی و مقداردهی اون با عدد در زبان C [اون تیکه malloc که نوشته شده داره میگه به من فلان قدر(نوشته به اندازه طول یه عدد) فضا داخل هیپ بده.]
تعریف یک متغیرِ ارجاعی و مقداردهی اون با عدد در زبان C [اون تیکه malloc که نوشته شده داره میگه به من فلان قدر(نوشته به اندازه طول یه عدد) فضا داخل هیپ بده.]


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

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

پس دیدیم همیشه اینطور نیست که هرجا آرایه دیدیم پس خودبخود میپره توی هیپ و همههههه‌ی عددا میرن توی استک.(۵*)

حالا که تا حد خوبی از انواع داده و انواع حالت ذخیره‌سازیشون اطلاعات پیدا کردیم بریم و علت ارور رو بررسی کنیم:

در مورد اون پشته‌ی فراخوانی(Call Stack) میخوام بعدا مفصل بنویسم ولی فعلا در این حد بگم که هر تابعی که تو JS اجرا می‌کنیم به جایی به اسم Call Stack اضافه میشه و وقتی هم که تموم بشه از پشته حذف میشه (چون ساختارش (FILO) هست؛ آخرین چیزی که وارد میشه اولین چیزیه که خارج میشه مثل استوانه قرص جوشان).

گفتیم که تو اون ارور احتمالا یه تابعی داره مدام خودشو صدا میزنه و فضا به استک اضافه می‌کنه(بهش میگن Stack Frame) و انقدر داره خودشو صدا میزنه و هی تو استک متغیر میریزه که عملا ما به ارور محبوب قلوب یعنی Stack Overflow میرسیم :) یعنی اینکه به مرز استک رسیدیم و برنامه دیگه نمیتونه متغیری تو استک ذخیره کنه.

جمع‌بندیِ پایانی:

فضای رم به ۲ بخش استک و هیپ تقسیم میشه و نحوه ذخیره‌سازی توی استک اینطوره که سایز متغیر رو میدونه و به همون اندازع پوینترش میره جلو و متغیر رو ذخیره میکنه و توی هیپ ساختار منظم نیست و داده‌ها همینطور پراکنده ذخیره میشن همچنین توی زبونای سطح بالا مثل JS خود زبان(۶*) تصمیم میگیره چیا برن توی هیپ و چیا توی استک که تو JS بهشون میگیم نوع داده‌ی اولیه و ارجاعی (ارجاعی از اسمش معلومه میره توی هیپ ذخیره میشه و آدرسش میره داخل استک و اولیه هم خود مقدارش میره توی استک)


امیدوارم تونسته باشم مفهومو تا حد خوبی رسونده‌ باشم و لطفا لطفا اگه جایی ایراد فنی داره یا با اطلاعات دیگه‌تون تداخل داره بهم بگید که منم بدونم و یاد بگیرم. 3>

* اگه خیلی دوست دارید بدونید بعضی از زبونا میشه مقداری که از سیستم‌عامل میخواید برای استک رو خودتون دستکاری کنید :)

** البته تا زمانی که RAM پر نشه دیگههه :) که معمولا نمیشه.

*** تو بعضی از زبونا مثل #C بهشون میگن نوع مقداری (Value type & Reference type)

۴* در واقع آرایه و فانکشن خودشون آبجکت محسوب میشن :)

۵* البته تو خود JS هم مثلا رشته‌ها میتونن توی هیپ ذخیره شن و همیشه اینطور نیست که برن توی استک :)

۶* توی JS مسئله یکم متفاوت‌تره چون engine ها هم میتونن نحوه پیاده‌سازیشون فرق کنه مثلا V8 بگه من اینطوری میکنم داده رو که بره توی هیپ و SpiderMonkey یه کار دیگه کنه.

منابع:

jsحافظهdata typesmemorystack and heap memory
شاید از این پست‌ها خوشتان بیاید