زمانی که یک root به یک شیی اشاره میکند شیی نمیتواند توسط Garbage Collector جمع آوری شود چون ممکن است برنامه به آن دسترسی پیدا کند. زمانی که یک root به یک شیی اشاره میکند، اصطلاحا گفته میشود که یک ارجاع قوی به آن شیی موجود است. با این وجود Garbage Collector همچنین از ارجاعات ضعیف هم پشتیبانی میکند. ارجاعات ضعیف به Garbage Collector اجازه می دهند که شیی را جمع آوری کند و همچنین به برنامه اجازه دهد که به آن شیی دسترسی داشته باشد.
اگر فقط ارجاعات ضعیف به یک شیی موجود باشند و Garbage Collector آغاز به کار کند آن شیی از heap پاک میشود و سپس زمانی که برنامه سعی کند به آن دسترسی پیدا کند این عمل با شکست مواجه میشود. به عبارت دیگر برای دسترسی به یک ارجاع ضعیف برنامه باید یک ارجاع قوی به آن داشته باشد. اگر برنامه قبل از اینکه Garbage Collector آن را جمع آوری کند یک ارجاع قوی به آن شیی تشکیل دهد، بنابراین Garbage Collector نمی تواند این شیی را حذف کند چون یک ارجاع قوی به آن شیی موجود است.
اجازه دهید معنی واقعی موضوع را در کد بررسی کنیم:
کد:
Void SomeMethod()
{
// Create a string reference to a new Object.
Object o = new Object();
// Create a strong reference to a short WeakReference object.
// The WeakReference object tracks the Object's lifetime.
WeakReference wr = new WeakReference(o);
o = null; // Remove the strong reference to the object.
O = wr.Target;
if(o==null)
{
// A garbage collection occurred and Object's memory was
// reclaimed.
}
else
{
// A garbage collection didn't occur and I can successfully
// access the Object using o.
}
}
ممکن است این سوال ایجاد شود که استفاده از ارجاعات ضعیف چه مزیتی را ایجاد میکند؟ خوب بعضی از ساختار داده ها به سادگی ایجاد میشوند اما مقدار زیادی حافظه را اشغال می کنند. برای مثال ممکن است شما برنامه ای داشته باشید که به لیستی از تمام دایرکتوری ها و فایلهای موجود در کامپیوتر نیاز داشته باشد. شما به سادگی می توانید یک درخت از تمام این اطلاعات تشکیل دهید و به محض اینکه برنامه شما اجرا شد به جای مراجعه به هارد به این درخت در حافظه مراجعه کند این امر سرعت برنامه شما را افزایش میدهد.
اما مشکل این است که این ساختار داده حجم بسیار زیادی را از حافضه اشغال می کند. اگر کاربر بخش دیگری از برنامه را آغاز کند، این درخت ممکن است دیگر مورد نیاز نباشد اما مقدار زیادی از حافضه را اشغال کرده است. شما می توانید ارجاع اصلی به این درخت در حافظه را رها کنید. اما اگر کاربر به قسمت اول برنامه شما برگشت، شما مجددا نیاز به بازسازی این درخت خواهید داشت. ارجاعات ضعیف این امکار ار به شما میدهد که به بهترین نحو این موقعیت را کنترل کنید.
زمانی که کاربر از قسمت اول برنامه شما خارج شود، شما می توانید یک ارجاع ضعیف به root مربوط به این درخت در حافظه ایجاد کنید و تمام ارجاعات قوی به آن را از بین ببرید. اگر حافظه برای بخشهای دیگر برنامه کم شد، Garbage Collector حافظه گرفته شده توسط درخت مذکور را می گیرد و زمانی که کاربر مجددا به قسمت اول برنامه برگشت و برنامه سعی کرد از ارجاع ضعیف یک ارجاع قوی بدست آورد، این تلاش شکست می خورد و برنامه مجددا درخت را تشکیل میدهد. در غیر این صورت برنامه از همان درخت قبلی موجود در حافظه استفاده می کند.
نوع داده System.WeakReference دو Constructor عمومی را ارائه میدهد:
کد:
کد:
public WeakReference(Object target);
public WeakReference(Object target, Boolean trackResurection);
پارامتر target شیی را که WeakReference باید نگه داری کند مشخص می کند. پارامتر trackResurrection مشخص می کند که آیا WeakReference باید شیی را حتی بعد از finalize شدن هم نگه داری کند یا خیر که معمولا مقدار این پارامتر false است.
برای راحتی یک ارجاع ضعیف را که بعد از احیای شیی هم آن را نگه داری میکند ارجاع ضعیف بلند و ارجاع ضعیفی را که بعد از احیای شیی آن را نگه داری نمی کند را ارجاع ضعیف کوتاه می نامیم. اگر نوع داده شیی تابع finalize را ارئه ندهد ارجاع ضعیف کوتاه و بلند مانند هم عمل می کنند. اما به شدت توصیه می شود که از به کار بردن ارجاعات ضعیف خودداری کنید، زیرا این نوع ارجاعات به شما اجازه میدهند که یک شیی را حتی بعد از finalize شدن هم احیا کنید که موجب به وجود آمدن یک موقعیت غیر قابل پیش بینی برای شیی می شود.
یک بار که شما یک ارجاع ضعیف را برای یک شیی ایجاد کردید عموما باید تمام ارجاعات قوی به آن را برابر null قرار دهید، در غیر این صورت Garbage Collector توانایی جمع آوری این شیی را در مواقع ضروری نخواهد داشت.
برای استفاده مجدد شیی شما باید ارجاع ضعیف را به یک ارجاع قوی تبدیل کنید. برای این کار شما می توانید از WeakReference.Target استفاده کنید و این شیی را به یکی از root های برنامه اختصاص دهید. اگر این عبارت مقدار null را برگرداند، یعنی شیی مذکور توسط Garbage Collector جمع آوری شده است، در غیر این صورت root حاوی آدرس یک ارجاع قوی به این مقدار محسوب می شود و برنامه از این طریق می تواند شیی مذکور را کنترل کند.