PDA

View Full Version : آیا اشیاء بعد از ایجاد نباید تخریب شوند یا اینکه؟



soroush_vs
چهارشنبه 28 تیر 1391, 14:38 عصر
با سلام

به قطعه کد زیر توجه کنید

SqlConnection con= new SqlConnection (connectionString);


قطعه کد بالا در یک تابع هست.

آیا نیاز هست که بعد از ایجاد شی ء con و استفاده ازش ، اون رو تخریب کنیم؟

اگر تخریب نشه آیا حافظه ای که با new به اون تخصیص داده شده آزاد میشه یا خیر؟
منظورم اینه که نحوه عمل C# تا چه حد با C++ متفاوت یا یکسان هست

TeacherMath
چهارشنبه 28 تیر 1391, 15:12 عصر
http://blogs.msdn.com/b/tess/archive/2007/04/10/net-garbage-collector-popquiz-followup.aspx

soroush_vs
چهارشنبه 28 تیر 1391, 15:33 عصر
.NET اون رو چه زمانی تخریب میکنه؟ در این زمینه اطلاع دارید؟

A.S.Roma
چهارشنبه 28 تیر 1391, 15:33 عصر
پیشنهاد میشه تنها در صورتی به شکل دستی Objectها Dispose بشن که از Unmanaged Resourceها استفاده شده باشه در غیر اینصورت این‌کار می‌تونه Performance Issue هم داشته باشه .

A.S.Roma
چهارشنبه 28 تیر 1391, 15:47 عصر
.NET اون رو چه زمانی تخریب میکنه؟ در این زمینه اطلاع دارید؟
CLR زمانی‌که هیچ Referenceی به اون Object نباشه این‌کار رو انجام میده. (Detail بیشتر خاظر نیست اما فکر کنم CLR در هر Pass Cycle از Statck‌ این‌کار رو انجام میداد)

soroush_vs
چهارشنبه 28 تیر 1391, 15:48 عصر
در مورد Unmanaged Resource سرچ زدم که در سی شارپ شامل دی ال ال ها یا فایل ها و غیره میشدن و برای استفاده از متغیر ها و کلاس های که با new ایجاد میشن اصلا نباید نگران حافظه بودو خارج از اسکوپ آزاد خواهند شد

mehdi.mousavi
چهارشنبه 28 تیر 1391, 17:07 عصر
Oooops! بچه ها اینایی که میگید رو از کجا آوردید؟ اگه تخریب بشه خوبه، اگه نشه خودش تخریب میکنه؟ Dispose کردن Object ها Performance Issue بهمراه داره؟ چی خارج از Scope (و کدوم Scope؟) آزاد میشه؟

سلام.
قبل از هر چیز، به این تصویر زیبایی که در Cacoo (https://cacoo.com/) رسم کردم دقت کنید:

89933

همونطوریکه می بینید، بحث Native App ای ها یه چیز، CLR Heap یه چیز دیگه، اگر چه در نهایت هر دو از سرویس های VMM استفاده می کنند. بنابراین ارتباطی بین مدیریت حافظه در برنامه های Native و Managed وجود نداره. با این توضیح، اجازه بدید قدری در مورد اون Box سبز رنگ صحبت کنم. حافظه در برنامه های Managed، به دو بخش Small Object Heap و Large Object Heap تقسیم میشه (از این پس از اولی تحت عنوان SOH و از دومی تحت عنوان LOH نام خواهم برد). SOH جایی هستش که Object هایی با اندازه 85KB (یا کمتر) در اون قرار میگیرن، در حالیکه Object هایی که نیاز به حافظه بیشتری دارن، در LOH قرار میگیرن.

SOH و LOH هر دو به بخش های کوچکتری به اسم Segment تقسیم میشن. به Segment اول در SOH، میگن Ephemeral Segment (یعنی بخش زودگذر) و اون جایی هستش که GC0 و GC1 رخ میده. (صبر داشته باشید، آروم آروم همه رو براتون توضیح میدم. امروز روز شانستون هستش که من کارم زیاد نیست). در SOH، حافظه Compact میشه، یعنی چی؟ یعنی فرض کنید این خونه های حافظه باشن: ABCDEFG .وقتی شما Object ای رو که Root اش، B هست رو آزاد می کنید و GC اونو Collect میکنه، در گام بعدی خانه های حافظه رو جابجا می کنه، بنابراین پس از آزادسازی B، خواهیم داشت: ACDEFG (یعنی فاصله خالی بین A و C نمی افته). اما همین مساله اگر در LOH رخ بده، در اونصورت خواهیم داشت: A-CDEFG.

حالا، وقتی شما میخواهید مجددا حافظه Allocate کنید، سیستم نگاه می کنه ببینه آیا به اندازه کافی، حافظه Contiguous داره یا خیر (یعنی حافظه همجوار). اگر حافظه همجوار داشته باشه، Allocation شما موفقیت آمیز خواهد بود. در غیر اینصورت، علیرغم اینکه ممکنه حافظه خالی زیادی روی سیستم بصورت گسسته وجود داشته باشه، اما چون Contiguous Memory وجود نداره، بنابراین Allocation مورد نظر Fail میشه. بنابراین، همواره باید سعی کنیم تا اختصاص حافظه از SOH صورت بگیره. الان اولین نکته این هستش که خوب چرا در LOH حافظه رو Compact نمی کنیم؟ پاسخ این هستش که LOH در حقیقت برای جلوگیری از کند شدن سرعت App (یا سیستم) بدلیل فشرده سازی حافظه در SOH بوجود اومد. احتمالا برخی از شما به خاطر دارید که فلان برنامه ای که نوشته بودید، در فواصل زمانی غیر قابل پیش بینی ای، UI برنامه بدلیل نامعلومی از کار می افتاده و برنامه Responsive نبوده. در نسخه های 1 و 1.1، این مشکل بشدت به چشم میومد. پس فهمیدیم حافظه در LOH فشرده نمیشه... (چون در اصل با این دید ایجاد شده که فقط سریع عمل کنه).

اما GC کی و در چه هنگامی کار Collect کردن رو شروع می کنه؟


وقتی استفاده عمومی از حافظه در سیستم، بالا باشه (برای این اینجا اینکارو میکنه که کل سیستم به زانو در نیاد).
به مرز نهایی تخصیص حافظه نزدیک بشیم.
برنامه نویس مستقیما GC.Collect رو Call کنه.

GC روی برنامه هایی که در Windows Server اجرا میشن، متفاوت از برنامه هایی که روی Workstation اجرا میشن، رفتار میکنه. در Workstation Mode، کلیه Thread های App متوقف میشن، Collection انجام میشه، سپس Thread ها Resume میشن (که منجر به حداقل تاخیر میشه). اما در برنامه های Server، هدف نهایی مکانیزم مورد استفاده "بالا بردن توان عملیاتی" هستش، چون نمیشه یک Web App رو از کار انداخت، کاربر چند ثانیه، دقیقه و ... معطل بشه و بعد پاسخش رو دریافت کنه... بنابراین در این Mode، اگرچه بازهم Thread ها Stop میشن، سپس Collect کردن Object ها شروع میشه، اما در Interval های خاصی، اجرای Thread ها Resume میشه تا سرویس بطور کل از کار نیفته. در نهایت، GC در Server Mode در GC Thread خاصی که وظیفه اش Collect کردن Object هاست، رخ میده در حالیکه در Workstation Mode، GC در Context اون Thread ای اجرا میشه که باعث شده Collection رخ بده.

حالا GC چطوری کار میکنه؟ GC طی سه مرحله، حافظه رو آزاد میکنه: Generation 0, 1, 2


GC0 حاوی Object هایی هستش که Short Lived هستن، یعنی پس از ایجاد، به سرعت به سیستم برمیگردن.
GC1 حاوی Object هایی است که Medium Lived هستن، یعنی از GC0 جون سالم به در بردن (چون هنوز Root دارن، ولی هنوز به GC2 نرسیدن.

دو مورد اخیر، چون در Ephemeral Segment هستش، سریعه.


GC2 وقتی رخ میده که Object هنوز در GC1 روت داره و آزاد نشده، بنابراین Long Lived Object ها در این Generation قرار میگیرن.

اما حالا GC از کجا متوجه میشه که یه Object ای root داره یا نه؟ (یعنی تهش به جایی وصله و هنوز قراره مورد استفاده قرار بگیره). بر اساس 4 مکانیزم، GC میتونه تشخیص بده که Object ای root داره یا خیر:


JIT Compiler
Handle Table
Finalize Queue
Stack Walker

(دقیق خاطرم نیست که ترتیبشون رو درست نوشتم یا نه، اما برای این بحث مهم نیست).

از اونجاییکه Unmanaged Resource ها، توسط CLR Heap Manager مدیریت نمیشن، پس CLR Manager راهی برای تشخیص یا عدم تشخیص میزان حافظه گرفته شده توسط Native API و نحوه آزاد سازی اون نداره. فرضا اگر n بایت حافظه از CLR Heap گرفته شد (برای یک Pointer)، اونوقت CLR Heap Manager همون n بایت رو آزاد خواهد کرد در صورتیکه ممکنه چند ده مگابایت اطلاعات توسط Native App در اون Pointer ذخیره بشه. بنابراین، شما موظفید، در Finalizer، منابع Native مورد استفاده در CLR Object خودتون رو آزاد کنید.

بعنوان آخرین نکته اینو هم بگم که در مورد Connection هایی که پرسیدید، مسائل دیگه ای نیز دخیل هستند. تنها در این حد بسنده میکنم که اگر Object ای IDisposable رو پیاده سازی کرده بود، (مثل همین SqlConnection) حتما Dispose رو روی اون فراخوانی کنید تا Connection به Connection Pool برگرده...

موفق باشید.

Saeed_m_Farid
چهارشنبه 04 مرداد 1391, 16:51 عصر
سلام
ببخشید ولی من هم سوال در این‌مورد داشتم؛ جناب mehdi.mousavi (http://barnamenevis.org/member.php?41233-mehdi.mousavi)، قبلاً هم یکبار (شاید هم بیشتر) شما وقت کرده بودین، در این‌مورد توضیح داده‌بودین؛ اونموقع خواستم سوال‌هام رو بپرسم ولی متاسفانه وقت نکردم حرفام رو جمع و جور کنم؛ حالا سوالاتی که برام بغرنج هستند رو می‌پرسم، هر وقت تونستید و وقت داشتین، بی‌زحمت من رو روشن کنید:



=+=+=+=+=+=+=+=+= I =+=+=+=+=+=+=+=+=
اما GC کی و در چه هنگامی کار Collect کردن رو شروع می کنه؟



وقتی استفاده عمومی از حافظه در سیستم، بالا باشه (برای این اینجا اینکارو میکنه که کل سیستم به زانو در نیاد).
به مرز نهایی تخصیص حافظه نزدیک بشیم.
برنامه نویس مستقیما GC.Collect رو Call کنه.

=+=+=+=+=+=+=+=+= II =+=+=+=+=+=+=+=+=
حالا GC چطوری کار میکنه؟ GC طی سه مرحله، حافظه رو آزاد میکنه: Generation 0, 1, 2



GC0 حاوی Object هایی هستش که Short Lived هستن، یعنی پس از ایجاد، به سرعت به سیستم برمیگردن.
GC1 حاوی Object هایی است که Medium Lived هستن، یعنی از GC0 جون سالم به در بردن (چون هنوز Root دارن، ولی هنوز به GC2 نرسیدن.


دو مورد اخیر، چون در Ephemeral Segment هستش، سریعه.



GC2 وقتی رخ میده که Object هنوز در GC1 روت داره و آزاد نشده، بنابراین Long Lived Object ها در این Generation قرار میگیرن.

=+=+=+=+=+=+=+=+= III =+=+=+=+=+=+=+=+=
بنابراین، شما موظفید، در Finalizer، منابع Native مورد استفاده در CLR Object خودتون رو آزاد کنید.



پیرو I: در این‌مورد من تو کتاب جفری ریچر خونده‌ام که GC غیر از GC.Collect، اینکار رو موقعی انجام میده که Generation 0 پر شده باشه، نه موقعی که حافظه پر شده باشه یا در آستانه پر شدن باشه[ا.]؛ بنابراین وقتی با chunk های خیلی کوچک متوالی که با تناوب خیلی کم به برنامه تحمیل میشن‌ (مثل کارهای سخت‌افزاری)؛ برنامه عملاً کارآئی ضعیفی پیدا میکنه، تا اونجایی که من می‌دونم محدودۀ Generation 0 حدوداً 256 KB هست و این حجم زیادی در کار با سیستم‌های ذکر شده هست؛ خوب سوالم اینه که آیا این دلیل میشه که ما برای کارهای سیستمی و یا سیستم‌های Embedded نباید از دات نت استفاده کنیم؟ یا مثلاً امکان داره که در فریم‌ورک NET Micro Framework (http://www.microsoft.com/en-us/netmf/default.aspx). (http://www.microsoft.com/en-us/netmf/default.aspx) جور دیگه‌ای Garbage Collector مدیریت شده باشه؟(با اینکه می‌دونم چنین چیزی یکمی نامعقول هست!)
پیرو II: تو Fundamentals of Garbage Collection (http://msdn.microsoft.com/en-us/library/ee787088.aspx) از MSDN من این اصطلاحات رو دیدم ولی فکر می‌کنم MSDN برای اینکه زیاد درگیر جزئیات نشه اینها رو بکار برده، تا بتونه به ذکر کامل Fundamentals برسه! سوالم در این‌مورد اینه که Garbage Collector از کجا Short Lived یا Medium یا Long بودن طول عمر Objec های ما رو متوجه میشه؟ تا اونجائی که من با ذهن ناقص‌ام از فصل 21 کتاب مذکور متوجه شدم CLR وقتی initialize میشه، برای هرکدوم از سه Generation مربوط به Garbage Collector یه بودجه‌ای(budget) اختصاص میده که معمولاً 256 KB برای Generation 0 و 2 MB برای Generation 1 و 10 MB برای Generation 2 هست و collect کردن وقتی انجام می‌گیره که این مرز رو رد کرده باشن[ب.]، حالا وقتی مثلاً متغیرهای محلی توابع ما، خیلی زود Root هاشون تو Managed Heap از بین میره، طبیعتاً Short-lived محسوب میشن ولی هیچ تضمینی هم روی اون نیست و Garbage Collector فقط Root میفهمه (مثلاً تو متغیرهای ارجاعی اینمورد صادق نیست)؛ خلاصه‌اش رو اینطوری می‌دونم: 90213 آیا درست فهمیدم که short lifetime و live longer و ... آبجکت‌های ما بر همین اساس تعیین میشن و Garbage Collector تعبیر درست از مدت زمان حیات Objectهای ما نداره؟ فقط Root می‌فهمه و اونهم همونطورکه تو تصویر مشخص هست، بعضی وقتا گیج میزنه! مخصوصاً تو generation 1 ...
پیرو III: من اول‌ها فکر می‌کردم این Finalizer همون مخرب تو C++‎‎‎‎‎‎‎‎‎‎‎/C هست (بخاطر شکل نوشتاری) ولی بعداً متوجه تفاوت‌های اساسی اون‌ها شدم و تو برنامه‌ها این مورد رو لحاظ کردم، اما یه چیزی هست که اصلاً نمی‌تونم در مورد این مفهوم متوجه بشم! اونم Resurrection هست، واقعیت‌اش درک این قضیه برام سخت هست و معنی‌اش رو نمی‌فهمم، یعنی دات نت یه امکانی گذاشته که اگه ما حواس‌مون نبود و Object مون مرد یا کشتیم‌اش، بتونیم احیاءاش کنیم و یه شیء Immortal درست کنیم؟ آدم یاد رستاخیز و اکثر این فیلم جدیدهای هالیوود میافته! مثلاً چنین سینتکسی کجا بدرد می‌خوره؟ internal sealed class SomeType {
~SomeType() {
Program.s_ObjHolder = this;
GC.ReRegisterForFinalize(this);
}
}

**********************
در آخر هم امکانش هست که این موضوع رو بیشتر باز کنیم تا بعضی ابهامات رفع بشه؛ می دونم شاید جاش اینجا نباشه ولی بالاخره در موردش صحبت شده و میشه ادامه‌اش داد، مگه اینکه بفرمائید جای دیگه تاپیک بزنم، بشرطی که به اعماق درّه تاپیک‌های #C نره! مثلاً در مورد کد زیر که آگاه‌سازی Garbage Collector در استفاده صحیح از منابع Native هست، من در مورد کارکرد HandleCollector ابهام دارم:
using System;
using System.Runtime.InteropServices;
public static class Program
{
public static void Main()
{
MemoryPressureDemo(0); // 0 causes infrequent GCs
MemoryPressureDemo(10 * 1024 * 1024); // 10MB causes frequent GCs
HandleCollectorDemo();
Console.ReadLine();
}

private static void MemoryPressureDemo(Int32 size)
{
Console.WriteLine();
Console.WriteLine("MemoryPressureDemo, size={0}", size);
// Create a bunch of objects specifying their logical size
for (Int32 count = 0; count < 15; count++)
{
Object o = new BigNativeResource(size);
Console.WriteLine(
" ==> [MemoryPressure] Generation is: {0}",
GC.GetGeneration(o));
}
// For demo purposes, force everything to be cleaned-up
GC.Collect();
GC.WaitForPendingFinalizers();
}

private sealed class BigNativeResource
{
private Int32 m_size;
public BigNativeResource(Int32 size)
{
m_size = size;
if (m_size > 0)
{
// Create Large object that must
//always considered part of generation 2!
Object o = new Byte[m_size];
// Display generation
Console.WriteLine(
" ==> [Physically BIG] Generation is: {0}",
GC.GetGeneration(o));

// Make the GC think the object is physically bigger
GC.AddMemoryPressure(m_size);
}
Console.WriteLine(
"BigNativeResource[{0}] create.",
m_size);
}
~BigNativeResource()
{
if (m_size > 0)
{
// Make the GC think the object released more memory
GC.RemoveMemoryPressure(m_size);
}
Console.WriteLine(
"BigNativeResource[{0}] destroy.",
m_size);
}
}

private static void HandleCollectorDemo()
{
Console.WriteLine();
Console.WriteLine("HandleCollectorDemo");
for (Int32 count = 0; count < 10; count++)
{
new LimitedResource();
}
// For demo purposes, force everything to be cleaned-up
GC.Collect();
GC.WaitForPendingFinalizers();
}

private sealed class LimitedResource
{
// Create a HandleCollector telling it that collections should
// occur when two or more of these objects exist in the heap
private static HandleCollector s_hc =
new HandleCollector("LimitedResource", 2);
public LimitedResource()
{
// Tell the HandleCollector that 1 more LimitedResource
// object has been added to the heap
s_hc.Add();
Console.WriteLine("LimitedResource create. Count={0}", s_hc.Count);
}
~LimitedResource()
{
// Tell the HandleCollector that 1 less LimitedResource
// object has been removed from the heap
s_hc.Remove();
Console.WriteLine("LimitedResource destroy. Count={0}", s_hc.Count);
}
}
}
______________________

[ا.]: نقل قول از کتاب CLR via C#‎‎‎‎‎‎‎‎‎, 3rd Edition (http://shop.oreilly.com/product/9780735627048.do) (ص. 546 مبحث Monitoring and Controlling the Lifetime of Objects Manually) :

http://barnamenevis.org/images/misc/quote_icon.png
Garbage collections do not occur when memory is full or close to full. Instead, garbage collections occur whenever generation 0 is full, which occurs approximately after every 256 KB of memory is allocated. So objects are being tossed out of memory much more frequently than desired, and your application’s performance suffers greatly.



[ب.]: نقل قول از همان (http://shop.oreilly.com/product/9780735627048.do) (ص. 554)



http://barnamenevis.org/images/misc/quote_icon.png
The managed heap supports only three generations: generation 0, generation 1, and generation 2; there is no generation 3. When the CLR initializes, it selects budgets for all three generations. As I mentioned earlier, the budget for generation 0 is about 256 KB, and the budget for generation 1 is about 2 MB. The budget for generation 2 is around 10 MB. Again, the budget sizes are selected to improve performance. The larger the budget, the less frequently a garbage collection will occur. And again, the performance improvement comes because of the initial assumptions: new objects have short lifetimes, and older objects are likely to live longer.