بخش اول این مقاله مفاهیم اصلی جمع آوری زباله و فرآیند مدیریت حافظه دات نت برای کدهای مدیریت شده را تشریح کرد. در این مقاله نحوه مدیریت کدهای مدیریت نشده در دات نت، Dispose Pattern بحث خواهد شد.
معرفی
پاکسازی
منابع مدیریت نشده : مدیریت و پاکسازی منابع مدیریت نشده.
CLR از مدیریت خودکار حافظه
(GC) پشتیبانی می کند. با این حال، منابعی غیر از حافظه مدیریت شده هنوز باید به طور صریح منتشر شوند و به عنوان منابع مدیریت نشده
شناخته می شوند ، مانند فایل های منابع سیستم عامل، ویندوز، اتصالات شبکه یا اتصالات پایگاه داده.
GC به طور خاص برای مدیریت چنین منابع مدیریت نشده طراحی نشده است،
به این معنی که مسئولیت مدیریت منابع مدیریت نشده در
دست توسعه دهندگان است.
راه های مختلفی برای پاکسازی منابع مدیریت نشده وجود دارد
،
- راه خودکار
توسط Finalizer و GC - راه دستی توسط Dipose Pattern
A: راه خودکار توسط Finalizer و GC
System.Object
یک روش مجازی Finalize
را اعلام می کند که توسط GC قبل از بازیابی حافظه شی توسط GC فراخوانی می شود و
می تواند برای آزاد کردن منابع مدیریت نشده لغو شود. انواعی که نهایی کننده را نادیده می گیرند به عنوان انواع نهایی
نامیده می شوند .
- متد Finalize به طور خودکار پس از غیرقابل دسترسی شدن یک شی فراخوانی می شود، مگر اینکه شما از شی در برابر نهایی شدن محافظت کرده باشید.
- متد Finalize به عنوان آخرین عملیات قبل از پایان برنامه نامیده می شود و غیر قطعی است. به طور معمول، اکثر متدهای Finalize در نقطه دیگری در طول اجرای برنامه اجرا می شوند و نه در انتهای آن.
توجه داشته باشید:
- ویرانگر
- سی شارپ به شدت از تخریبگرها پشتیبانی نمی کند
- سی شارپ از نادیده گرفتن روش Object.Finalize که از نظر نحوی با یک تخریبگر به عنوان ~Classname یکسان است، پشتیبانی می کند .
- کامپایلر همه کدها را در "ویرانگر" ( finalizer ) یا نادیده گرفتن Finalize در یک بلوک try و بلوک ترکیبی در نهایت شیء کلاس پایه را فراخوانی می کند. Finalize
- معایب استفاده از Finalizer
- نهایی کننده زمانی فراخوانی می شود که GC تشخیص دهد که یک شی برای جمع آوری واجد شرایط است. این در یک دوره زمانی نامشخص پس از اینکه دیگر به منبع مورد نیاز نیست اتفاق می افتد. تأخیر بین زمانی که توسعهدهنده میتواند یا میخواهد منبع را منتشر کند و زمانی که منبع واقعاً توسط نهاییکننده منتشر میشود، ممکن است غیرقابل قبول باشد .
- هنگامی که CLR نیاز به فراخوانی نهایی کننده دارد، باید جمع آوری حافظه شی را تا دور بعدی جمع آوری زباله به تعویق بیندازد (نهایی کننده ها بین مجموعه ها اجرا می شوند). این بدان معنی است که حافظه شی (و همه اشیایی که به آنها اشاره می کند) برای مدت زمان طولانی تری آزاد نمی شود.
ب: روش دستی با الگوی دور ریختن
دو مرحله برای انجام وجود دارد :
- الگوی دفع را اجرا کنید . این امر مستلزم ارائه یک IDisposable.Dispose برای فعال کردن انتشار قطعی منابع مدیریت نشده است. یک مصرف کننده از نوع شما زمانی که شی (و منابعی که استفاده می کند) دیگر مورد نیاز نیست، Dispose را فراخوانی می کند. روش Dipose بلافاصله منابع مدیریت نشده را آزاد می کند .
- در صورتی که مصرف کننده ای از نوع شما فراموش کند که Dispose را فراخوانی کند، راهی برای انتشار منابع مدیریت نشده خود ارائه دهید . دو راه برای انجام این کار وجود دارد ،
- روش Object.Finalize را لغو کنید (در مفهوم).
- از یک دسته ایمن برای بسته بندی منابع مدیریت نشده خود (در عمل) استفاده کنید .
توجه داشته باشید:
نادیده گرفتن روش Object.Finalize برای نشان دادن مفهومی که در این مقاله به آن خواهیم پرداخت خوب است. با این حال، مایکروسافت هشدار می دهد که این روش "می تواند پیچیده و مستعد خطا باشد". در عمل باید از روش Safe handle استفاده کنیم که در مقاله بعدی قسمت سوم به آن خواهیم پرداخت تا بتوانیم در اینجا بر مفهوم روش دفع تمرکز کنیم.
موارد زیر را یکی یکی بحث خواهیم کرد
- 1: رابط کاربری IDisposable.
- 2: متد Dispose();
- 3: روش GC.SuppressFinalize;
- 4: روش Object.Finalize را لغو کنید
- 5: انواع یکبار مصرف;
- 6: مصرف کننده از نوع دور ریختنی;
1 - رابط کاربری IDdisposable
Framework رابط کاربری System.ID
را فراهم می کند
- public interface IDisposable
- {
- void Dispose();
- }
که فقط شامل یک متد است: Dispose();
2 - روش Dispose().
رابط IDisposable
باید با روش Dispose() پیادهسازی شود تا راهی دستی برای
آزادسازی قطعی منابع مدیریتنشده بهمحض عدم
نیاز به آنها ارائه کند.
- public void Dispose()
- {
- …… // Dispose the unmanaged resources
- }
3 – روش GC.SuppressFinalize
روش Dispose همچنین روش GC.SuppressFinalize
را ارائه می دهد که می تواند به GC بگوید که یک شی به صورت دستی از بین رفته است و
دیگر نیازی به نهایی شدن ندارد، در این صورت می توان حافظه شی را
زودتر بازیابی کرد.
- public void Dispose()
- {
- …… // Dispose the unmanaged resource
- GC.SuppressFinalize(this);
- }
4 – روش Object.Finalize را لغو کنید
نهاییسازی، انتشار غیر قطعی منابع مدیریتنشده را هنگامی که مصرفکننده یک نوع قادر به فراخوانی IDisposable نیست، امکانپذیر میکند. با نادیده گرفتن متد Object.Finalize یک نهایی کننده تعریف کنید.
- public class MyClass
- {
- ~MyClass()
- {
- // Do unmanaged resource clean up here
- Console.WriteLine("In destructor");
- }
- }
توجه داشته باشید
اگر به سادگی روش Finalize را نادیده بگیریم،
- public class MyClass
- {
- protected override void Finalize()
- {
- // Do unmanaged resource clean up here
- }
- }
کار نمی کند، یک خطای کامپایل دریافت خواهید کرد:
پیشنهاد این است که "شیء را لغو نکنید. نهایی کنید. در عوض، یک ویرانگر فراهم کنید.» همانطور که در A بحث کردیم، "C# از نادیده گرفتن روش Object.Finalize پشتیبانی می کند که از نظر نحوی با یک مخرب به عنوان ~Classname یکسان است ."
5 – انواع یکبار مصرف
انواعی که رابط IDisposable
را پیاده سازی می کنند به عنوان نامیده می شوند
انواع یکبار مصرف.
- public class MyClass : IDisposable
- {
- private string name;
- public MyClass(string name) { this.name = name; }
- override public string ToString() { return name; }
- // call Dispose() in Finalizer, i.e., 'Destructor'
- ~MyClass()
- {
- Dispose();
- Console.WriteLine("~Thing()");
- }
- // Implementation of IDisposable.
- // Call the virtual Dispose method.
- // Suppress Finalization.
- public void Dispose()
- {
- Console.WriteLine("Dispose()");
- GC.SuppressFinalize(this);
- }
- }
6 – مصرف کننده انواع یکبار مصرف
- public class GarbageDisposalApp
- {
- [STAThread]
- public static void Main(string[] args)
- {
- DoSomething();
- Console.WriteLine("end of Main");
- Console.ReadLine();
- }
- public static void DoSomething()
- {
- MyClass t = new MyClass("Foo");
- Console.WriteLine(t);
- t.Dispose(); // toggle this to see the difference
- t = null;
- GC.Collect();
- GC.WaitForPendingFinalizers();
- }
- }
نکاتی برای الگوی دفع
- الگوی Dispose برای استاندارد کردن
استفاده و اجرای نهایی کننده
و رابط IDdisposable برای آزاد کردن
منابع خارجی در نظر گرفته شده است.
- دلیل اصلی الگوی Dispose این است که
مشتری یک شی را قادر می سازد تا آن را به صورت دستی از بین ببرد ، در حالی که اطمینان حاصل شود که شی دقیقاً یک بار در طول عمرش Disposed شده است.
خلاصه
در این مقاله نحوه مدیریت کدهای مدیریت نشده در دات نت، Dispose Pattern بحث شده است.
الگوی دفع به طور خلاصه:
- در اصل،
شما باید Dispose را از Destructor فراخوانی کنید و در Dispose باید
Finalize را Suppress کنید.
- هنگامی که یک متد Dipose را به درستی پیادهسازی میکنید، اگر متد Dipose فراخوانی نشود،
متد Finalize به یک محافظ برای پاک کردن منابع تبدیل میشود .
به طور خلاصه تفاوت بین الگوی نهایی کردن و دور ریختن ،
- متد Finalize در مرحله ای از اجرای برنامه قبل از نقطه پایانی به طور خودکار فراخوانی می شود . روش دفع باید به صورت دستی فراخوانی شود .
- برای جلوگیری از اجرای دستی dispose
و سپس اجرای مجدد نهایی کننده به صورت خودکار، یا عدم اجرای آن
، باید متد dispose
را از روش finalize فراخوانی کنیم و روش Finalize را با GC.SuppressFinalize از روش dispose حذف کنیم .
در این مقاله، ما بر روی مفهوم تمرکز می کنیم، با استفاده از روش نادیده گرفته شده Object.Finalize
، در مقاله بعدی، قسمت سوم، در مورد روش دسته ایمن به تفصیل بحث خواهیم کرد .