در کنسول دارکوب نمودار مصرف مموری اپ نمایش داده میشود. میخواهیم در این مقاله در مورد این نمودار و خطای Out Of Memory یا به اختصار OOM صحبت کنیم. ابتدا کمی با کرنل و مدیریت مموری در آن آشنا میشویم تا بتوان به شکل عمیقتری به مصرف مموری پرداخت. سپس متریکی را که در نمودار دارکوب نمایش داده میشود توضیح میدهیم. در نهایت در مورد OOM صحبت خواهیم کرد.
دادههای پردازه در مموری
برنامههایی که در یک سیستم عامل اجرا میشوند برای نگهداری و تعریف متغیرها، خواندن فایلها و دستورات اجرایی خود نیاز به مموری دارند. هر برنامه مجموعهای از چند پردازه در سیستم عامل است. مموری یا RAM یک نوع حافظه موقت است که که یک پردازه برای کار کردن به آن نیاز دارد. در ادامه در مورد انواع دادههای منتسب به یک پردازه روی مموری و نامگذاری آنها صحبت میکنیم.
زمانی که یک پردازه شروع به کار میکند، دستورات اجرایی یا همان کد آن به همراه فایلها و متغیرهایی که لازم دارد توسط کرنل در مموری قرار داده میشوند. در طول اجرای پردازه ممکن است متغیرهای جدیدی تعریف شوند. این متغیرها یا در stack قرار میگیرند یا heap. این دو هم بخشی از مموری پردازه را تشکیل میدهند.
اگر متغیری به طور موقتی در یک تابع معرفی شود، در stack قرار میگیرد و بعد از اجرای تابع در مموری قابل دسترس نیست. اگر متغیر عمر بیشتری از یک تابع داشته باشد و بین threadهای مختلف پردازه مشترک باشد، از heap برای آن استفاده میشود.
در کنار اینها مصارف دیگری هم از مموری وجود دارد. خیلی از برنامهها از کتابخانههای مشترک سیستم عامل یا shared library استفاده میکنند. لود کردن این کتابخانهها هم مقداری مموری مصرف میکند. گاهی بین دو پردازه مموری مشترک یا shared memory وجود دارد؛ یعنی هر دو آن قسمت از مموری را میبینند و از آن طریق با هم ارتباط برقرار میکنند.
نکته دیگری که باید در مورد مموری در نظر داشت این است که گاهی کرنل، دادههای موجود در مموری مورد استفادهی پردازهها را در دیسک نگه میدارد. این اتفاق زمانی میافتد که مموری کل سیستم جای خالی نداشته باشد و پردازهای به مموری بیشتر نیاز داشته باشد.
در این زمان کرنل قسمتهایی از مموری که کمتر از بقیه مورد استفاده هستند را روی دیسک مینویسد و آن را از مموری پاک میکند. مموری آزاد شده به پردازهای که آن را نیاز دارد میرسد. به این کار swap کردن میگویند و به بخشی از دیسک که این دادهها روی آن قرار میگیرند swap گفته میشود. دقت کنید قسمت swap شده از مموری خود متعلق به یک پردازه است و اگر به آن نیاز داشته باشد دوباره به مموری باز میگردد.
خود کرنل هم برای کارایی بهتر دادههایی را در مموری نگه میدارد که جزو مموری مصرفشده پردازه حساب میشوند. یکی از آنها page cache است که کرنل (بهطور دقیقتر فایلسیستم) پیجهایی از فایلهای روی دیسک را در مموری نگه میدارد تا برای هر بار خواندن و نوشتن فایل توسط پردازه، دسترسی به دیسک نیاز نباشد. این کار سرعت خواندن و نوشتن فایل را افزایش میدهد؛ زیرا مموری بسیار سریعتر از دیسک است.
توجه کنید که page cache برای بهبود عملکرد سیستم استفاده میشود و در صورتی که فشار روی مموری باشد میتوان آن را به قیمت کاهش سرعت حذف کرد (بعد از نوشتن pageها در دیسک). دیگر مورد buffer دادههایی است که توسط درایورها قرار است به دستگاههای مختلف فرستاده شود؛ مثلا برای فرستادن بهتر دادهها به دیسک آنها را در قالبهای 20 مگابایتی جمعآوری میکند تا یکجا به دیسک بفرستد. buffer حجم زیادی ندارد (در حدود 20 مگابایت مثلا) و موقتی است.
در این قسمت در مورد دادههایی که مربوط به یک پردازه هستند صحبت کردیم. البته تمام موارد گفته نشد و سعی شد مهمترین آنها گفته شود. قسمت بعد در مورد نامگذاریهای مختلف مموری مصرفی یک پردازه است.
نامگذاریهای مموری مصرفی پردازه
این قسمت را از این جهت آوردیم که برای بخشهای مختلف مموری مصرفی پردازه نامهای متفاوتی وجود دارد. برای مثال RSS که مخفف Resident Set Size است یا VSZ که مخفف Virtual Memory Size است. RSS بخشی از مموری یک پردازه است که واقعا در RAM حضور دارد (به این معنی که swap نشده یا اگر فایل است در page cache قرار دارد) و توسط پردازه استفاده شده است.
شامل کتابخانههای مشترک، page cache، مموری مشترک، heap، کد برنامه و stack است. گاهی پیش میآید که پردازه مموری را allocate میکند ولی بخشی از آن را مصرف نمیکند. در این حالت پردازه میتواند از بخش مصرف نشده هم استفاده کند ولی چون هنوز استفاده نکرده جزو RSS محسوب نمیشود. یعنی روی مموری پیجی به آن اختصاص داده نشده است. این مورد و مموری swap شده جزو VSZ محسوب میشوند که عدد بزرگتری از RSS است.
بخشی از مموری RSS یک پردازه همان طور که گفته شد از page cache است. یعنی pageهایی از فایلهای روی دیسک که در مموری هستند. برخی از این پیجها به عنوان پیج غیرفعال در کرنل به حساب میآیند؛ چون اخیرا پردازهای از آنها استفاده نکرده است. در صورتی که کمبود مموری وجود داشته باشد میتوان آنها را پاک کرد و در صورت نیاز در دیسک نوشت. به همین دلیل عدد دیگری از مصرف مموری پردازه وجود دارد که WSS یا Working Set Size است. اگر تعداد پیجهای مربوط به page cache که غیرفعال هستند را از RSS کم کنیم WSS به دست میآید. در نتیجه WSS از RSS کوچکتر است.
نمودار مصرف مموری در دارکوب
یک نمونه نمودار مموری را در تصویر زیر میبینید:
در مورد واحد مموری باید تفاوت بین واحدهای توان ۲ و توان ۱۰ را در نظر گرفت. واحدهایی که توان ۲ هستند یک حرف i در خود دارند؛ مثلا KiB یا Kibibyte که ۲ به توان ۱۰ یا ۱۰۲۴ بایت است. یا MiB که ۲ به توان ۲۰ است. واحدهای توان ۱۰ سادهتر و رایجترند؛ مثل KB یا Kilobyte که ۱۰ به توان ۳ یا ۱۰۰۰ بایت است. یا MB که ۱۰ به توان ۶ بایت است. چون واحدهای توان ۲ اعداد بزرگتری نسبت به واحد متناظر توان ۱۰ هستند، عددی که با واحد توان ۲ گفته میشود کوچکتر است. برای مثال:
1 MiB = 1.049 MB
1 GiB = 1.074 GB
عددی که در نمودار نشان داده میشود WSS است. ابزاری این عدد را از کرنل گرفته و برای هر کانتینر آن را ارائه میکند. ابزار مانیتورینگ هم هر چند ثانیه یک بار (مثلا ۱۰ ثانیه) آن را خوانده و ذخیره میکند. خط افقی نمایش داده شده حداکثر میزان مموری مجاز برای کانتینر است. این عدد همان عددی است که موقع ساخت اپ برای مموری انتخاب میکنید.
وقتی پردازههای داخل کانتینر بخواهند بیشتر از حد مجاز تعیین شده مموری مصرف کنند و کرنل نتواند پیجی برای آن آزاد کند، اصطلاحا کانتینر Out Of Memory یا OOM میشود. در این وضعیت کرنل یکی از پردازههای کانتینر را kill میکند. کرنل با استفاده از تعدادی معیار سعی میکند بدترین پردازه را از نظر مصرف مموری پیدا کرده و kill کند.
اگر همان پردازهی اصلی کانتینر انتخاب شود خطای OOMKill را میبینید و کانتینر ریاستارت میشود. ولی اگر پردازههای دیگر انتخاب شوند، این اتفاق رخ نمیدهد و دنبال کردن آن هم سخت است.
نکتهای که باید در بررسی OOM توجه شود این است که نمودار مصرف مموری لزوما به حد مجاز مصرف (همان خط افقی ثابت) نمیرسد؛ چون ممکن است مثلا ۵۰ مگابایت جا داشته باشد ولی درخواست ۶۰ مگابایت مموری وجود داشته باشد. در این حالت هم یکی از پردازهها kill میشود چون کرنل نمیتواند این درخواست را برآورده کند.
جمعبندی
در این مقاله در مورد مموری یک پردازه در کرنل توضیح دادیم. سپس دیدیم نمودار دارکوب چه چیزی را نمایش میدهد و در نهایت مفهوم OOM را بررسی کردیم. امیدواریم با خواندن این مقاله دید بهتری نسبت به مموری در کرنل و نمودار ارائه شده پیدا کرده باشید.