PDA

مشاهده نسخه کامل : سئوال درباره ی GC در سی شارپ



SajjadKhati
21-12-17, 18:05
سلام
چرا در کد زیر :



int b = 300000;
Button[] a = new Button[b];


for (int i = 0; i < a.Length; i++)
{
a[i] = new Button();
a[i] = null;
}

a = null;
GC.Collect();


GC میتونه حافظه را آزاد کنه ولی در کد زیر که اعضای آرایه را در حلقه ی جداگانه ی دیگه ای null میکنم ، نمیتونه حافظه را آزاد کنه؟ :



int b = 300000;
Button[] a = new Button[b];


for (int i = 0; i < a.Length; i++)
{
a[i] = new Button();
}
for (int i = 0; i < a.Length; i++)
{
a[i] = null;
}


a = null;
GC.Collect();

ravegoat
03-01-18, 08:46
سلام سجاد جان،

ببخشید با تاخیر دارم جواب میدم.

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

در کد دوم هم GC در حین پیشروی حلقه ها به اجرا در میاد. با این تفاوت که در حلقه ی اول تنها اشیا تولید میشن ولی ارجاعی نسبت به اون ها وجود داره (در حلقه ی دوم ما نسبت به این اشیا ارجاع داریم). در نتیجه GC نمی تونه چیزی رو Collect کنه و حافظه ی مصرفی افزایش پیدا می کنه. حلقه ی دوم نسبت به حلقه ی اول بسیار سریع تر اتفاق می افته. در این حالت GC تا به خودش بیاد که کدوم اشیا خالی شدن، حلقه ی دوم تموم شده. حالا GC می مونه و کلی اشیا که باید جمع آوری کنه. GC برای جمع هر شی، Finalizer اون رو صدا می زنه. متد Finalizer برای کلاس Button به نسبت زمان بره. تو این حالت GC این اشیا رو داخل یک صف قرار میده تا اجرای Finalizer هاشون به اتمام برسه. با دستور GC.WaitForPendingFinalizers ما صبر می کنیم تا این صف خالی باشه. در نهایت اگر مجددا دستور GC.Collect رو صدا بزنیم می بینیم که حافظه مقداری مشابه کد اول خواهد داشت.

برای اطلاعات بیش تر:
Only the registered members can see the link(v=vs.110).aspx
Only the registered members can see the link
Only the registered members can see the link
C# Tips & Tricks: Weak References - When and How to Use Them (Only the registered members can see the link)
Only the registered members can see the link

SajjadKhati
03-01-18, 09:24
سلام سجاد جان،

ببخشید با تاخیر دارم جواب میدم.

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

در کد دوم هم GC در حین پیشروی حلقه ها به اجرا در میاد. با این تفاوت که در حلقه ی اول تنها اشیا تولید میشن ولی ارجاعی نسبت به اون ها وجود داره (در حلقه ی دوم ما نسبت به این اشیا ارجاع داریم). در نتیجه GC نمی تونه چیزی رو Collect کنه و حافظه ی مصرفی افزایش پیدا می کنه. حلقه ی دوم نسبت به حلقه ی اول بسیار سریع تر اتفاق می افته. در این حالت GC تا به خودش بیاد که کدوم اشیا خالی شدن، حلقه ی دوم تموم شده. حالا GC می مونه و کلی اشیا که باید جمع آوری کنه. GC برای جمع هر شی، Finalizer اون رو صدا می زنه. متد Finalizer برای کلاس Button به نسبت زمان بره. تو این حالت GC این اشیا رو داخل یک صف قرار میده تا اجرای Finalizer هاشون به اتمام برسه. با دستور GC.WaitForPendingFinalizers ما صبر می کنیم تا این صف خالی باشه. در نهایت اگر مجددا دستور GC.Collect رو صدا بزنیم می بینیم که حافظه مقداری مشابه کد اول خواهد داشت.

برای اطلاعات بیش تر:
Only the registered members can see the link(v=vs.110).aspx
Only the registered members can see the link
Only the registered members can see the link
C# Tips & Tricks: Weak References - When and How to Use Them (Only the registered members can see the link)
Only the registered members can see the link

سلام
ممنون استاد آرمین
یعنی باید متد GC.WaitForPendingFinalizers() ، بعد از GC.Collect() فراخونی بشه؟
اگه آره ، کد را بصورت زیر تغییر دادم ولی کار نکرد :



int b = 300000;
Button[] a = new Button[b];




for (int i = 0; i < a.Length; i++)
{
a[i] = new Button();
}
for (int i = 0; i < a.Length; i++)
{
a[i] = null;
}




a = null;

GC.Collect();
GC.WaitForPendingFinalizers();


قبل اش هم گذاشتم ولی بازم کار نکرد
اگه نه ، کد بالا باید چجوری تغییر کنه تا کار کنه؟

ravegoat
03-01-18, 09:36
نه اون دو خط آخر این طوری میشه:


GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

SajjadKhati
03-01-18, 12:20
آها ممنون استاد آرمین :11():
پس اگه اشتباه نکنم ، متد GC.Collect() که اولین بار صدا زده میشه ، چون منتظر جمع آوری و کارهای مربوط به GC نمیمونه (حالا شاید اون عملیات در یک نخ دیگه اجرا میشه) ، پس نمیتونه اطلاعات را پردازش کنه و کارشو تموم کنه ولی متد GC.WaitForPendingFinalizers() ، باعث توقف اون نخی که اطلاعات را جمع آوری میکنه میشه تا عملیات مربوط به صف ، در GC تمام بشه و بعد که دوباره GC.Collect() را فراخونی میکنیم ، چون اطلاعات مطمئنا جمع آوری شده ، کارشو تموم میکنه و درست انجام میده
میدونم تحلیل ام غلط داره چون از عملکرد GC و توابع هاش چندان اطلاعاتی ندارم
ولی حدودا درست هه؟

ravegoat
03-01-18, 14:36
سجاد جان بنده همون جملات خودت رو باز نویسی می کنم:


متد GC.Collect() که اولین بار صدا زده میشه ، چون منتظر جمع آوری و کارهای مربوط به GC نمیمونه (اون عملیات چون Async هستش قطعا در یک نخ دیگه اجرا میشه). متد GC.WaitForPendingFinalizers() ، باعث توقف نخ جاری میشه (نخی که این دستور را صدا زده که مطابق مثال همون Main Thread برنامه هستش) تا عملیات مربوط به صف ، در GC تمام بشه و بعد که دوباره GC.Collect() را فراخونی میکنیم ، چون اطلاعات مطمئنا جمع آوری شده ، کارشو تموم میکنه و درست انجام میده.


منبع (Only the registered members can see the link(v=vs.110).aspx)