تاریخچهای مختصر از GC در NET.
ایده اولیه ایجاد Garbage Collector در NET.، در سال 1990 بود که در آن زمان، مایکروسافت مشغول پیاده سازی خود از JavaScript بنام JScript بود. در ابتدا JScript توسط تیمی چهار نفره توسعه داده میشد و در آن زمان یکی از اعضای این تیم به نام Patrick Dussud که بعنوان پدر Garbage Collector در NET. شناخته میشود، یک Conservative GC را داخل تیم توسعه داد. در آن زمان CLR ای وجود نداشت و Patrick Dussud برروی JVM کار میکرد.مایکروسافت سعی بر پیاده سازی نسخهای اختصاصی از JVM را برای خود بجای ایجاد چیزی شبیه به NET Runtime. فعلی داشت؛ اما بعد از شکل گیری تیم CLR، به این نتیجه رسیدند که JVM برای آنها محدودیتهایی را ایجاد میکند و به همین دلیل شروع به ایجاد Environment خود کردند.
با این تصمیم، Patrick Dussud مجددا یک GC جدید را با ایده "بهترین GC ممکن" با زبان LISP که در آن بیشترین مهارت را داشت، بصورت Prototype نوشت و سپس یک Transpiler از LISP را به ++C نوشت که کدهای آن قابل استفاده در Runtime مایکروسافت باشد.
کدهای فعلی مربوط به Garbage Collector مورد استفاده در NET. در این فایل از ریپازیتوری runtime مایکروسافت قابل دسترسی هستند. در حال حاضر خانم Maoni Stephens مدیر فنی تیم GC مایکروسافت هستند که کنفرانسها و مقالات زیادی نیز درباره نکات مختلف پیاده سازی GC در بلاگ خود نوشته و ارائه کردهاند.
در حال حاضر، سه حالت (flavor) از GC در NET. تعبیه شدهاست و هرکدام از این حالات برای انواع مختلفی از برنامهها بهینه شدهاست که در ادامه به بررسی آنها میپردازیم.
Server GC
این نوع GC برای برنامههای سمت سرور نظیر ASP.NET Core و WCF بهینه سازی شدهاست که تعداد ریکوئستهای زیادی به آنها وارد میشود و هر ریکوئست باعث allocate شدن اشیا مختلفی شده و بطور کلی، نرخ allocation و deallocation در آنها بالاست. Server GC به ازای هر پردازنده، از یک Heap و یک GC Thread مجزا استفاده میکند. این بدین معناست که اگر شما یک پردازنده را با هشت Core داشته باشید، در زمان Garbage Collection، روی هرکدام از Coreها یک Heap و GC Thread مستقل وجود دارد که عمل Garbage Collection را انجام میدهند.
این شکل عملکرد باعث میشود که Collection، در سریعترین زمان ممکن، بدون وقفه اضافه انجام شود و برنامه شما اصطلاحا ((Freeze)) نشود.
Server GC فقط روی پردازندههای چند هستهای قابل اجراست و اگر سعی کنید برنامه خود را روی یک سیستم با پردازنده تک هستهای در حالت Server GC اجرا کنید، بصورت خودکار برنامه شما از Non-Concurrent Workstation GC استفاده کرده و اصطلاحا Fallback خواهد شد.
اگر نیاز دارید که در برنامههایی بهغیر از Server-Side Applicationها، نظیر WPF و Windows Serviceها و ... از این نوع GC استفاده کنید (به شرط چند هسته بودن پردازنده)، میتوانید این تنظیمات را به فایل app.config یا web.config خود اضافه کنید:
<configuration> <runtime> <gcServer enabled="true"/> </runtime> </configuration>
همچنین در برنامههای NET Core.ای نیز میتوانید این تنظیمات را داخل فایل csproj. برنامه خود اضافه کنید:
<PropertyGroup> <ServerGarbageCollection>true</ServerGarbageCollection> </PropertyGroup>
Concurrent Workstation GC
این حالت، حالت پیشفرض مورد استفاده در برنامههای Windows Forms و Windows Service است. این حالت از GC برای برنامههایی بهینه شدهاست که در آنها، هنگام وقوع Garbage Collection، برنامه توقف و مکث حتی چند لحظهای نداشته و Collection باعث نشود که کاربر نتواند روی یک دکمه کلیک کند و اصطلاحا برنامه ((Unresponsive)) شود.
برای فعالسازی Concurrent Workstation GC این تنظیمات را داخل config برنامه خود باید اعمال کنید:
<configuration> <runtime> <gcConcurrent enabled="true" /> </runtime> </configuration>
Non-Concurrent Workstation GC
این حالت شبیه به حالت Server GC است؛ با این تفاوت که عمل Collection روی Thread ای که درخواست allocate کردن یک object را کرده است، صورت میگیرد.
برای مثال:
- Thread شماره یک درخواست allocate کردن یک string با طول 10000 کاراکتر را میدهد.
- حافظه، فضای کافی برای تخصیص این حجم از حافظه را نداشته و سعی میکند با اجرای Garbage Collector، این حجم فضای مورد نیاز از حافظه را خالی کند.
- CLR تمام Threadهای برنامه را متوقف میکند و Garbage Collector شروع به کار کرده و اشیا بلااستفاده «روی Thread ای که آن را فراخوانی کرده است» را Collect میکند.
- بعد از پایان Collection، تمامی Threadهای برنامه که در مرحله قبل متوقف شده بودند، مجددا شروع به کار خواهند کرد.
این حالت از GC برای برنامههای Server-Side ای که برروی پردازنده تک هستهای اجرا میشوند، پیشنهاد میشود. برای فعالسازی این حالت، تنظیمات داخل config برنامه به این صورت باید تغییر پیدا کند:
<configuration> <runtime> <gcConcurrent enabled="false" /> </runtime> </configuration>
این جدول، کمک خواهد کرد که بر اساس نوع برنامه خود، تنظیمات درستی را برای GC اعمال نمایید (در اکثر موارد، تنظیمات پیشفرض بهترین انتخاب بوده و نیازی به تغییر روند کار GC نیست):
Server GC | Non-Concurrent Workstation | Concurrent Workstation | |
Maximize throughput on multi-processor machines for server apps that create multiple threads to handle the same types of requests. | Maximize throughput on single-processor machines. | Balance throughput and responsiveness for client apps with UI. | Design Goal |
1 per processor ( hyper thread aware ) | 1 | 1 | Number of Heaps |
1 dedicated GC thread per processor | The thread which performs the allocation that triggers the GC. | The thread which performs the allocation that triggers the GC. | GC Threads |
EE is suspended during a GC. | EE is suspended during a GC. | EE is suspended much shorter but several times during a GC. | Execution Engine Suspension |
<gcServer enabled="true"> | <gcConcurrent enabled="false"> | <gcConcurrent enabled="true"> | Config Setting |
Non-Concurrent Workstation GC | On a single processor (fallback) |