PDA

View Full Version : سوال: Dispose و لزوم استفاده از آن



babakj
دوشنبه 16 دی 1387, 12:34 عصر
1- آیا لازمه که در کلاسهایی که خودمون می سازیم متد Dispose را Implements نماییم ؟

2- چه هنگامی لازم است که از Dispose استفاده کنیم ؟

* متاسفانه من در نت هر چی جستجو کردم همه در مورد کانکشن صحبت کردند و مثلا کسی نگفته وقتی شما یک کلاس USER داری بعد از استفاده باید آنرا Dispose نمایید

3- حال اینجا این سئوال است که اگر دات نت خود مدیریت حافظه را بر عهده دارد و از GC پشتیبانی می کند چرا برخی از کلاسهای خودش متد Dispose را Implements کرده است ؟

Alireza Orumand
دوشنبه 16 دی 1387, 13:31 عصر
سلام
ببین دوست من منابع در دات نت به دو دسته تقسیم میشن.
1- مدیریت شده
2- مدیریت نشده
در مورد منابع مدیریت شده که تکلیف معلومه. خود دات نت مدیریت اون منابع را برای شما انجام میده ولی بعضی منابع مثل reader یا فایل ها مدیریت نمیشن. یعنی اگر شما خودتون reader یا فایل را close نکنید دات نت هم زحمت این کارو برای شما نمیکشه.
اینجاست که نیاز به راه حل احساس میشه. البته که خود شما هر جا از این منابع استفاده کردید باید حواستون باشه ولی اگه حواستون نبود دوتا راه پیش روی شما هست.
1- استفاده از destructor
2- پیاده سازی IDesposable و متد Despose
و در این متد ها تمامی منابع مدیریت نشده خودتون رو آزاد کنید. اینطوری حتی اگر فراموش هم بکنید این کار رو جایی انجام بدید باز هم منبع آزاد خواهد شد.
پیشنهاد میشه از هر دو راه استفاده کنید.
منبع: Wrox Professional C#2008
موفق باشید.

RoostaYeBekr
دوشنبه 16 دی 1387, 15:16 عصر
با سلام
چند وقت پیش یک نفر تو همین سایت یک کدی فرستاده بود ، به شکل زیر :


آیا این بیهوده نبوده که متغیرهای محلی dt و da را هم Dispose کرده ؟ آخه به نظر من اصلا لازم نبود . چون طول عمر متغیرهای محلی ، داخل همان متدی است که تعریفشان می کند. با تمام شدن کار متد ، طول عمر این متغیرهای محلی هم تمام می شود.



protected void Button1_Click(object sender, EventArgs e)
{
#region show xml
myclass convert = new myclass();

System.Data.SqlTypes.SqlXml xx = new System.Data.SqlTypes.SqlXml();
x = new Xml();
string conn = @"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirector y|\Database.mdf;Integrated Security=True;User Instance=True";
SqlConnection myConnection = new SqlConnection(conn);
string comm = "SELECT [id], [title], [meaning], [translate] FROM [word]";
SqlCommand myCommand = new SqlCommand(comm, myConnection);
myConnection.Open();
DataTable dt = new DataTable();
SqlDataAdapter da = new SqlDataAdapter(comm, conn);
da.Fill(dt);
mydive.InnerHtml = @"<?xml version="1.0" encoding="utf-8" ?>";
mydive.InnerHtml += "<br />";
mydive.InnerHtml +="&lt;book&gt;";

for (int i = 0; i < dt.Rows.Count; i++)
{
mydive.InnerHtml += "<br />";
mydive.InnerHtml += @"&lt;word&gt;";
mydive.InnerHtml += "<br />";
if (dt.Rows[i]["title"] != null) { mydive.InnerHtml += @"&lt;title&gt;"; mydive.InnerHtml += convert.CovertToHtml(dt.Rows[i]["title"]); mydive.InnerHtml += @"&lt;/title&gt;"; mydive.InnerHtml += "<br />"; }
if (dt.Rows[i]["meaning"] != null) { mydive.InnerHtml += @"&lt;meaning&gt;"; mydive.InnerHtml += convert.CovertToHtml(dt.Rows[i]["meaning"]); mydive.InnerHtml += @"&lt;/meaning&gt;"; mydive.InnerHtml += "<br />"; }
if (dt.Rows[i]["translate"] != null) { mydive.InnerHtml += @"&lt;translate&gt;"; mydive.InnerHtml += convert.CovertToHtml(dt.Rows[i]["translate"]); mydive.InnerHtml += @"&lt;/translate&gt;"; mydive.InnerHtml += "<br />"; }

mydive.InnerHtml += @"&lt;/word&gt;";
mydive.InnerHtml += "<br />";
} mydive.InnerHtml += "<br />";
mydive.InnerHtml += @"&lt;/book>";
mydive.InnerHtml += "<br />";
x.DocumentContent = mydive.InnerText;
dt.Dispose();
da.Dispose();
myCommand.Dispose();
#endregion
}

babakj
دوشنبه 16 دی 1387, 16:42 عصر
سلام
ببین دوست من منابع در دات نت به دو دسته تقسیم میشن.
1- مدیریت شده
2- مدیریت نشده
در مورد منابع مدیریت شده که تکلیف معلومه. خود دات نت مدیریت اون منابع را برای شما انجام میده ولی بعضی منابع مثل reader یا فایل ها مدیریت نمیشن. یعنی اگر شما خودتون reader یا فایل را close نکنید دات نت هم زحمت این کارو برای شما نمیکشه.
اینجاست که نیاز به راه حل احساس میشه. البته که خود شما هر جا از این منابع استفاده کردید باید حواستون باشه ولی اگه حواستون نبود دوتا راه پیش روی شما هست.
1- استفاده از destructor
2- پیاده سازی IDesposable و متد Despose
و در این متد ها تمامی منابع مدیریت نشده خودتون رو آزاد کنید. اینطوری حتی اگر فراموش هم بکنید این کار رو جایی انجام بدید باز هم منبع آزاد خواهد شد.
پیشنهاد میشه از هر دو راه استفاده کنید.
منبع: Wrox Professional C#2008
موفق باشید.

سئوال مهم :
خوب حالا فرض کنید من یک کلاس دارم که اون کلاس توسط یکسری Property مقادیری را می میگیرد محاسبانی انجام می دهد و در آخر آن مقادیر را وارد بانک می کند
من مدیریت کانکشن و SqlCommand و همه چیز را در آن کلاس به خوبی انجام دادم
آیا این کلاس مدیریت شده است ؟ آیا هنگامیکه ما از این کلاس استفاده می کنیم و بعد کارمان با این کلاس تما می شود باید آنرا Dispose کنیم ؟
-------------------------------------------------------------------------------


با سلام
چند وقت پیش یک نفر تو همین سایت یک کدی فرستاده بود ، به شکل زیر :


آیا این بیهوده نبوده که متغیرهای محلی dt وda را هم Dispose کرده ؟ آخه به نظر من اصلا لازم نبود . چون طول عمر متغیرهای محلی ، داخل همان متدی است که تعریفشان می کند. با تمام شدن کار متد ، طول عمر این متغیرهای محلی هم تمام می شود.


خوب دقیقا منم می خوام همین رو بگم حالا در مورد منابع مدیریت نشده به قول این دوستمون
da , dt رو می زاریم جزو اونها اصلا
ولی در مورد این کلاسی که من گفتم چی ؟

ببینید این خیلی مسئله مهمی است - فکر کنم نباید از کنارش ساده گذشت

بذارید بگم چرا مهمه ....

فرض کنید شما برای یه نفر یک برنامه می نویسید و اون بابا کلی هم حال می کنه و برنامه خوب و عالی کار می کنه مثلا یه برنامه حسابداری است

بعد موقع کار یهو هوس می کنه IE رو run کنه یه گشتی هم توی اینترنت بزنه بعد کلی brower tab باز می کنه و بعد یهویی یادش می افته FireFox هم باز کنه چون سرعتش بالاتره
حین کار هم مشعول گوش کردن موسیقی با winamp میشه

یهو می بینه سیستمش کند شده در صورتی که مثلا photoshop رو با این همه برنامه run میکنه هیچ بلایی سر سیستمش نمی آد اون هر چقدر هم یه کاربر غیر حرفه ایی باشه می فهمه که برنامه ما یک مشکلی داره و واقعا مشکلی به اسم عدم مدیریت صحیح حافظه داره و خلاصه فحش رو نثار برنامه نویس میکنه !

خیلی از برنامه هایی که دوستان یا شرکت ها می نویسند یا حتی خودمون می نویسیم و تست می کنیم در نگاه اول عالی کار می کنه مخصوصا روی سیستم های امروزی که حداقل 1 GB حافظه داریم ... من فکر می کنم مشکلی که اینجا خیلی از دوستان هم دارند و گله می کنند که آقا برنامه ما کند داره کار می کنه به همین دلیل بر می گرده و بهتر است به این مهم بیشتر پرداخته بشود و اگر دوستان مقاله ایی یا دستور العمل کاملی دارند ارائه بدهند

من پیرو پست شماره یک دوستمون آقای Alireza Orumand (http://barnamenevis.org/forum/member.php?u=50030) فکر می کنم در مورد منابع مدیریت شده و مدیریت نشده بیشتر صحبت بشه خیلی خوبه و لطفا این سوال مهم منم پاسخی منطقی بدهید .

سپاسگذارم

SMRAH1
سه شنبه 17 دی 1387, 14:58 عصر
سلام

بحث مدیریت حافظه ،یکی از بحث های مهم در عرصه نرم افزار نویسی است.مثلا شرکت موزیلا بعد از FIreFox2 یک فراخوان برای برنامه نویس ها میده تا مشکل استفاده از حافظه زیاد موقع بارگذاری رو کم کنه (و انصافا توی FireFox3 موفق هم بوده).این مطلب اونقدر در مورد این نرم افزار مهم بوده که طرفدارای دو آتشه IE این مشکل رو به عنوان اولین ایراد FireFox مطرح می کردند!
با این موضوع که در نرم افزار های داخلی هم این مطلب رعایت نمیشه (البته معمولا) موافقم.شاید به این دلیل که برنامه نویسی که داره برنامه رو می نویسی ،خودش روی یک سیستم آنچنانی (از نرم افزار تا سخت افزار) کار می کنه و نمی دونه بعضی ها می خواهند این نرم افزار رو روی یک سیستم با حداقل امکانات اجرا کنند! به همین دلیل سینه چاک های VB زیادن و VC کارها کم! در هر حال بگذریم....

اما دوستان به منابع مدیریت شده (که مقصود مدیریت توسط GC) و منابع مدیریت نشده (که همچنان کمافی السابق باید توسط برنامه نویس مدیریت بشه - مثل فایلها ، Connection ها و ...) اشاره کردند.ولی قبل از این که در مورد این دو سئوال که 'آیا این بیهوده نبوده که متغیرهای محلی dt وda را هم Dispose کرده' و 'آیا این کلاس مدیریت شده است' با هم بحث کنیم.می خواهم یک بار دیگر مراحل کار GC رو مرور کنیم (هر چند همه می دونیم ولی برای استناد به اون به تکرار نیازه!).درضمن این مطالب برای انواع دارای نوع Refence است نه انواع مقدار (مثل int یا struct و ..).

1) وقتی از کلمه کنیدی new استفاده می کنید،CLR یک فضا برای اون تخصیص داده و به GC اعلام می کنه کی متغیر جدید داره و یک شمارنده Refrence برای اون ایجاد میکنه!
2) اگر این فضای تخصیص داده شده به متغیری تخصیص داده بشه (مثلا متغیری که به این فضا اشاره دارد به متغیر دیگری تخصیص داده بشه)،به شکل اتوماتیک به شمارنده Refrence افزوده می شه.به همین دلیل است که دات نت اجازه پیاده سازی عملگر انتساب (=) رو به برنامه نویسی نمی ده (در یک کلاس همه عملگر ها به غیر از انتساب قابل پیاده سازی هستند!).
3) هنگامی که یک متغیر نابود می شه (مثلا یک متغیر محلی که با خروج از تابع نابود میشه)،یا متغیری که قبلا یک Refrence به محلی از حافظه داشته،حالا اشاره به محل دیگه ای کنه (با انتساب مقدار دیگه ای به اون)،به شکل اتوماتیک از شمارنده Refrence کاسته می شه.توجه کنید که به محض به صفر رسیدن این شمارنده ،فضای مورد بجث آزاد نمیشه!
4) هر گاه GC اقدام به پاک سازی حافظه کنه (اینجاست که زمان این کار معلوم نیست) ،دو عملیات رخ میده اول اینکه فضا هایی که شمارنده Refrence اونها صفر است ،رها سازی می شوند.دوم تمام اطلاعات موجود در حافظه دوباره باز آرایی میشوند تا فضا های مورد استفاده در کنار هم قرار گیرند (بحث Boxing هم مربوط به این کار GC است).البته برنامه نویس با برنامه متوجه جابجایی اطلاعات در حافظه نمی شوند و عملا با همان Refrence که در متغیر ها هستند دسترسی به مقادیر ممکن است!(باید بگم این بحث کاملتر از این توضیح کوچک است ولی تا همینجا کافی به نظر می رسه).

حالا سئوال اینه که 'کی GC اقدام به رها سازی حافظه می کنه (مرحله4)؟'.زمان این عمل معلوم نیست (درضمن این عملیات دریک Thread مستقل از Thread اصلی انجام میشه).شاید چند ثانیه نیز طول بکشه! پس نتیجه میگیریم که بین مراحل سوم و چهارم ،با اینکه دیگر نیاز به حافظه مورد نحث نیست،ولی هنوز در حافظه مکانی برای آن منظور شده.شاید برای متغیر های کوچک ،خیلی مهم نباشد ولی برای متغیر هایی با حافظه مصرفی بزرگ،قطعا می تواند نگران کننده باشد.
برای این کار دو راه حل را میشناسم (راه های دیگری هم شاید باشد که من از آن بی خبر باشم).راه اول استفاده از متد Dispose اشیای است.در این حالت فضای مصرفی آنها تا حد امکان کوچک میشود.مثلا کد زیر را در یکی از صفحات همین سایت دیدم :

public void PaintGradient(Control _control, LinearGradientMode _direction, Color _gradientColorStart, Color _gradientColorEnd)
{
//http://barnamenevis.org/forum/showpost.php?p=468043&postcount=35
LinearGradientBrush gradBrush;
gradBrush = new LinearGradientBrush(new Rectangle(0, 0, _control.Width, _control.Height), _gradientColorStart, _gradientColorEnd, _direction);
Bitmap bmp = new Bitmap(_control.Width, _control.Height);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(gradBrush, new Rectangle(0, 0, _control.Width, _control.Height));
_control.BackgroundImage = bmp;
_control.BackgroundImageLayout = ImageLayout.Stretch;
}به نظر شما فراخوانی یک متد مثل Paint برای یک کنترل چند بار صورت می گیرد.شاید چندین بار در یک ثانیه.البته در یک پیاده سازی بد می توان این مقدار را به چند هزار بار در یک ثانیه رساند (توجه کنید که فراخوانی یک متد مثل این، کاملا به پیاده سازی وابسته است).همینطور که ملاحظه می کنید شی gradBrush هرگز نابود نمی شود تا وقتی که GC به حسابش برسد! حالا اگر فرض کنید که در یک برنامه این متد چند ده بار در ثانیه فراخوانی شود و GC نیز 5 ثانیه یک بار عمل کند (تکرار می کنم : این که GC کی عمل کند به خیلی عوامل وابسته بوده و قابل پیش بینی نیست!).در نتیجه تا زمانی که GC عمل کند ،چه مقدار حافظه،بی مورد مصرف شده است!در حالی که در انتهای این متد می توان با یک خط ساده مثل


gradBrush.Dispose();این حافظه را به سیستم برگرداند!در این متد فقط یک متغیر اینگونه است.ممکن است در برنامه های بزرگتر،تعداد متغیر ها هم بیشتر و هم فضای مورد استفاده آنها بزرگتر و تعداد فراخوانی آنها بیشتر باشد! پس بهتر است در کلاس هایمان اولا عادت کنیم که متد Dispose را پیاده سازی کنیم و ثانیا در انتهای هر متد متغیر های بلا مصرف را Dispose کنیم (کار از محکم کاری عیب نمی کند!).توجه کنید که مخرب کلاس (destructor) در فرایند مرحله 4 GC فراخوانی می شود.
راه حل دوم هم استفاده از متدهای خوده GC است مثل Collect و WaitForPendingFinalizers (این متد Thread اصلی را می خواباند تا فرآیند پاک سازی حافظه انجام شود.به عبارت دیگر یک جور فعال سازی GC برای مرحله 4 است)،که معمولا به شکل

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();مورد استفاده می گیرند.حتی متدی مثل KeepAlive که ان روی سکه مدیریت حافظه است،نیز می تواند در جای خود موثر باشند!

تا جایی که سوادم اجازه داد سعی کردم مسئله رو باز کنم.
موفق و سربلند باشید.

محمدامین شریفی
سه شنبه 17 دی 1387, 17:13 عصر
با سلام
چند وقت پیش یک نفر تو همین سایت یک کدی فرستاده بود ، به شکل زیر :


آیا این بیهوده نبوده که متغیرهای محلی dt وda را هم Dispose کرده ؟ آخه به نظر من اصلا لازم نبود . چون طول عمر متغیرهای محلی ، داخل همان متدی است که تعریفشان می کند. با تمام شدن کار متد ، طول عمر این متغیرهای محلی هم تمام می شود.

سلام رفیق
همه کارهای این شخص بیهوده است!
جدیدا من اینکار رو میکنم:

#region fill data table
try
{
using (SqlConnection con = new SqlConnection(kontor.Properties.Settings.Default.f eizConnectionString))
{
SqlDataAdapter dap = new SqlDataAdapter("SELECT parvande, staf1firstName, staf1lastName, staf2firstName, staf2lastName, [day], [month], [year], visitting FROM staf", con.ConnectionString);
datatable1 = new DataTable();
dap.Fill(datatable1);
}
}
catch (Exception ex) { MessageBox.Show(ex.ToString()); }

#endregion
توجه : datatable را global تعریف کرده ام.
یکی از دلایلی که من object هام رو نابود میکنم به این خاطر است که کاربر اگر از دوباره روی button کلیک کرد با خطا روبرو نشود.اگر connection را نابود نکنیم باید آنرا ببندیم یا اینکه در موقع کدنویسی ببنیم که connection باز است یا بسته.
در مورد فایل ها هم همینگونه است.
این بسیار روشن است که 2 شئی نمیتوانند که به یک shared resource همزمان دسترسی پیدا کنند.چون اگر یکی از آنها بخواهد منبع را پاک کند و دیگری میخواهد آن اطلاعات پاک شده را بخواند.بنابرین #C مانع از اینکار میشود.
برای پیگیری این بحث باید بروید مبحث garbage collector را بخوانید.
ولی اینرا بدانید هر وقت با IO و SQL کار میکنید بعد از پایان کارتان آنها را حتما نابود کنید.

پیروز باشید.

RoostaYeBekr
پنج شنبه 19 دی 1387, 17:00 عصر
با سلام
SMRAH1 (http://barnamenevis.org/forum/member.php?u=44567) ، باز هم ممنون.
SMRAH1 (http://barnamenevis.org/forum/member.php?u=44567) گفتید :
وقتی از کلمه کنیدی new استفاده می کنید، ...............
حال سوالم این است که آیا برای متغیرهای محلی که از کلمه ی new استفاده نمی کنند هم ، هنگام خروج از متد تعریف کننده ی آنها ، حافظه ی اشغالی توسط آنها پس داده نمی شود ؟ ( که البته خود من بعید می دانم که پس داده نشود . چون خیلی جاها در تعریف duration یا life time یا طول عمر متغیر ، دیده ایم که گفته اند : مدت زمانی است که در حافظه وجود دارد. بعضی از متغیرها ، طول عمر کوتاهی دارند . بعضی از آن ها دائما ایجاد و حذف می شوند و بعضی دیگر تا انتهای برنامه وجود دارند. حوزه ی متغیر یا scope ، جایی است که به نام متغیر می تواند در برنامه مراجعه شود .بعضی از متغیرها در سراسر برنامه قابل مراجعه اند ، در حالی که به بعضی دیگر فقط در بخشی از برنامه می توان مراجعه کرد. و نهایتا در تعریف طول عمر متغیرهای محلی ، دیده ایم که گفته اند : تا زمانیکه متد دربرگیرنده فعال است ، این متغیرها وجود دارند و وقتی کنترل برنامه از آن متد خارج شد ، این متغیرها از بین می روند و به این خاطر ، گاهی به این متغیرها ، متغیرهای دارای طول عمر اتوماتیک هم می گویند . منبع : کتاب C#.Net آقای جعفرنژادقمی )

و اینکه راهی ( کدنویسی ) وجود ندارد که خود برنامه نویس کنترل GC را بعهده بگیرد ؟

RoostaYeBekr
پنج شنبه 19 دی 1387, 17:13 عصر
راستی آقای محمد امین
کد جدیدتان را دیدم . یک مشکلی به نظرم دارد که البته شاید من اشتباه می کنم که در تاپیک زیر مطرح کرده ام :
http://barnamenevis.org/forum/showthread.php?t=136286

Sajjad.Aghapour
پنج شنبه 19 دی 1387, 19:31 عصر
سلام
یه سوال هم من دارم.....
آیا وقتی حافظه ای که یک متغیر میگیره ،پس از نابودی اون این عمل در Mem Usage در Task Manager قابل مشاهده هست یا نه؟

SMRAH1
پنج شنبه 19 دی 1387, 19:51 عصر
سلام

مبحث مهمي در برنامه نويسي دات نت است (قبلا اشاره شد) که در مورد انواع داده ها است که من اون ها رو به انواع داراي 1) نوع Refence است و 2) نوع مقدار مي نامم (نام گذاريهاي فارسي متفاوتند).در نوع مقدار يا Value Type،هر گاه شما اقدم به تعريف متغير کنيد عملا فضايي براي آن در حافظه تخصيص داده مي شود.انواع معمولي مثل int و float و ... حتي Struct ها هم از اين دسته اند(البته به غير از دو نوع string و object).اينها اصلا نيازي به Dispose کردن ندارند.اما انواع ديگر،که شامل string و object و تمام کلاسهاي (دات نت يا تعريف شده توسط برنامه نويس) و آرايه ها (حتي امثال []int) ،با تعريف متغير يک نوع از آنها ساخته نميشود و صريحا بايد توسط new آن را ايجاد و تخصيص داد.البته توجه کنيد که در دو نوع مستثناي string و object نکات ظريف ديگري نيز هستند.مثلا هنگامي که در کد مي نويسيم:

string str = "A";عملا کامپايلر يک نوع string ايجاد کرده و به آن رشته مذبور را تخصيص مي دهد.
هر چند نکات ظريف ديگري نيز هستند اما به نظر مي رسد که همين پاسخ کفايت کند.

همچنين،هر زمان حوزه ي متغير يا scope تمام شود ،فضاي آن آزاد مي شود (اگر با new باشد رها سازي آن به GC مربوط است).

اما راه هاي کنترل GC را هم در انتهاي پست قبليم (راه حل دوم) اشاره شده.هر چند که مزايا و معايبي (مثل به خواب رفتن Thread جاري تا پايان پاک سازي حافظه) نيز دارد.

موفق باشيد

محمدامین شریفی
پنج شنبه 19 دی 1387, 20:43 عصر
آقای SMRAH1 (http://www.barnamenevis.org/forum/member.php?u=44567) جان.
من شنیدم که تا وقتی reference ای به شئی باشد GC آنرا پاک نمیکند،به این دلیل آرایه و اشاره گر ها هم فرق میکنند.
در ضمن مگر اشیای IO و Data با new ساخته نمیشوند؟اما پس از بیرون آمدن از scope هنوز نابود نشده اند.
SMRAH1 (http://www.barnamenevis.org/forum/member.php?u=44567) جان object به این دلیل که یک متغییر دات نت نیست و یک low level است.فرق میکند .
حالا اگر درون struct از object استفاده کنیم چی؟
SMRAH1 (http://www.barnamenevis.org/forum/member.php?u=44567) جان در مورد stringbuilder قضیه چگونه است؟

---------------------------

SMRAH1 (http://www.barnamenevis.org/forum/member.php?u=44567) جان ببخشید که اینقدر سوال کردم.چون میدونم حرفه ای هستی پرسیدم

RoostaYeBekr
پنج شنبه 19 دی 1387, 23:48 عصر
آقای SMRAH1 (http://www.barnamenevis.org/forum/member.php?u=44567) جان.
من شنیدم که تا وقتی reference ای به شئی باشد GC آنرا پاک نمیکند.



خوب درسته . من توی یکی از پست هام ، صحبت از متد کردم و متغیر محلی . به خاطر همین احتمالا منظور ایشان از پاک کردن ، جواب دادن صحبت من بوده .

راستی من خودم هم یک سوال برام بوجود آمد و آن اینکه با وجود اینکه StringBuilder هم از نوع new است ، اما برنامه متد Dispose را برای آن نمی شناسد.
-------------
اینکه آیا با نوشتن تنها GC.Collect();
در آخر یک متدی که دارای متغیر محلی است ، مشکل پس دادن حافظه حل نمی شود؟

RoostaYeBekr
جمعه 20 دی 1387, 00:19 صبح
البته فکر کنم حساب این StringBuilder از بقیه جداست . همانطور که می دانیم این StringBuilder ، یک خاصیت داره به نام Capacity ، که ظرفیت شیء کلاس StringBuilder را تعیین می کند. ( خاصیت های مشابه هم دارد مثل خاصیت MaxCapacity ).
از همین جا می تونیم نتیجه بگیریم که ظرفیت شیء ای که از StringBuilder ساخته می شود ، توسط برنامه نویس ، قابل تنظیم است .
کد زیر را به برنامه ی خود اضافه کنید و breakpoint اش را هم بگذارید تا نتیجه را ببینید :


int j = 0;
System.Text.StringBuilder sb = new System.Text.StringBuilder();
int x1 = sb.Length;
int x2 = sb.Capacity;
for (j = 0; j < 1000; j++)
{
sb.Append("PSQ_");
sb.ToString();
}
int m1 = sb.Length;
int m2 = sb.Capacity;
sb.Remove(0, m);
int k1 = sb.Length;
int k2 = sb.Capacity;


اصلا علت استفاده ی از StringBuilder به جای string ، زمانی است که می دانیم قرار است به تعداد دفعات زیاد ، پردازش های متعدد روی رشته مان قرار گیرد. چون همانطور که می دانیم زمانی که به یک string مقداری داده می شود ، آن مقدار قبلی از بین نمی رود ، بلکه Pointer به یک جای دیگر اشاره می کند و اگر پردازش روی آن رشته زیاد باشد ، سربار زیادی ایجاد می شود. اصلا علت استفاده از StringBuilder به جای string همین است.

محمدامین شریفی
جمعه 20 دی 1387, 09:25 صبح
به نظر من علت استفاده از این متد(نه متغییر)اینست که ما طول رشته معینی نداریم.
و ادامه اش گفته شما:
علت استفاده ی از StringBuilder به جای string ، زمانی است که می دانیم قرار است به تعداد دفعات زیاد ، پردازش های متعدد روی رشته مان قرار گیرد. چون همانطور که می دانیم زمانی که به یک string مقداری داده می شود ، آن مقدار قبلی از بین نمی رود ، بلکه Pointer به یک جای دیگر اشاره می کند و اگر پردازش روی آن رشته زیاد باشد ، سربار زیادی ایجاد می شود. اصلا علت استفاده از StringBuilder به جای string همین است.
گرچه این مبحث جدای GC بحث میباشد.اگر کسی علاقه مند به این بحث است اینجا (http://barnamenevis.org/forum/showthread.php?t=46514) را دنبال کند.
اینم برنامه رفیق خودم:قلب:

Sajjad.Aghapour
جمعه 20 دی 1387, 13:27 عصر
آیا وقتی حافظه ای که یک متغیر میگیره ،پس از نابودی اون این عمل در Mem Usage در Task Manager قابل مشاهده هست یا نه؟

....
آیا لزومی بر پیاده سازی Destructor یا پیاده سازی Dispose در یک کلاس هست یا نه؟

محمدامین شریفی
جمعه 20 دی 1387, 14:11 عصر
....
آیا لزومی بر پیاده سازی Destructor یا پیاده سازی Dispose در یک کلاس هست یا نه؟
پرسش اولیت رو جوابش رو نمیدونم سجاد جون،ولی تاپیک کلا برای پرسش دومیت ایجاد شده!

Sajjad.Aghapour
جمعه 20 دی 1387, 17:04 عصر
همون دیگه....
سوال اولی منو گیج کرده که این منابع چطوری پاک میشن.میتونید امتحان کنید....
وقتی روی یک دکمه کلیک کنید.حالا داخل رویداد اون دکمه یک شی از یک کلاس که نسبتا کار سنگینی رو انجام میده ایجاد کنید به حجم استفاده از حافظه اضافه میشه.....
ولی با وجود Scope برای این کلاس بعد از اجرا از حجم حافظه کاسته نمیشه.
نمیدونم این دوتا به هم ربطی دارن یا نه.اگه یه نفر بگه که ربطی دارن یا نه لطف بزرگی به این بنده حقیر کرده؟ D:

SMRAH1
جمعه 20 دی 1387, 19:02 عصر
سلام

1) بله ،تا وقتي reference اي به شي باشد، GC آنرا پاک نميکند اما آن را در يک جاي ثابت در حافظه هم نگه نمي دارد بلکه در حافظه جابجا مي کند (يک جور Defrag در RAM پس از رها سازي حافظه هايي که ديگر نياز نيست رخ مي دهد.).مفهوم Boxing در اينجا مطرح مي شود.
2) اشياي IO نيز نابود مي شوند ولي قسمتي از منابع که بايد برنامه نويس کنترل کند مربوط به حافظه (RAM) نيست.مثلا در يک IO ، ممکن است شما فايلي را باز کنيد.پس از اتمام کار بايد فايل را بست.در واقع باز کردن فايل خود يک منبع است.اين مفهوم براي Connection ها هم صادق است.بايد بعد از Open کردن و انجام کار بايد آن را Close کرد.همانطور که مي بينيد اي مفاهيم مستقل از ايجاد يک متغير هستند.به همين دليل به آنها منابع مديريت نشده مي گويند.
3) object در واقع همان کلاس Object است (string هم همان کلاس String).
4) در يک ساختار (struct) به شکل پيش فرض متغير ها مقدار دهي مي شوند.مثلا يک شي object با مقدار پيش فرض null يا يک int با عدد صفر ، bool با False و ....البته مي توان آنها را در صورت نياز تغيير داد(در متد هايي در داخل struct يا خارج آن).حتي مي توان سازنده اي براي struct ساخت(!) که فراخواني آن مستلزم استفاده از کلمه جادويي new در معرفي ساختار است يعني:

MyStruct My = new MyStruct();
5) دستور GC.WaitForPendingFinalizers فرايند پاک سازي را انجام مي دهد اما توجه به اين نکته ضروري است که در اين حالت Thread اصلي به خواب مي رود تا فرايند پاک سازي پايان يابد.به عبارت ديگر اگر داده هاي پاک شده زياد باشد يک hang لحظه اي در برنامه خواهيد داشت.
6) StringBuilder متد Dispose ندارد شايد به اين دليل که احتياج نيست (فضاي زيادي را اشغال نمي کند).در هرحال نمي داند چرا! شايد مربوط به همان MaxCapacity باشد!
7) اينکه 'آيا وقتي حافظه اي که يک متغير ميگيره ،پس از نابودي اون اين عمل در Mem Usage در Task Manager قابل مشاهده هست يا نه؟' : نمي دانم! شايد بهتر باشد اين طوري مسئله مطرح شود : آيا حافظه کاري برنامه (که توسط GC کنترل مي شود) ار ابتدا دريافت و پر يا خالي بودن آن به عهده GC است يا هر گاه قسمتي از آن مورد نياز نبود به ويندوز باز گردانده مي شود؟ و بعدا دوباره درخواست مي گردد؟
9) اينکه آيا نياز به پياده سازي Destructor يا پياده سازي Dispose هست يا نه ،بايد اشاره کنم که اين دو کاري واحد ولي در واقع دو کار کرد مجزا دارند!مخرب يک کلاس زماني فراخواني مي شود که GC اقدام به رها سازي حافظه آن کند (زمان آن مشخص نيست) ولي Dispose را معمولا برنامه نويسي فراخواني کرده تا همان فرايند (البته ممکن است تحت شرايطي خاص تر) انجام شود.در هر حال هر دو زمينه را براي نابودي مناسب و مطلوب شي مهيا مي سازند.اما پياده از اين دو (با توجه به کار کرد آن) از ديد من در کلاس هايي مفيد است.که اولا داراي حافظه مصرفي زياد هستند (مثلا کلاس، متغير هاي متعدد و آرايه هاي حجيم دارد يا ...) و يا ثانيا داراي منابع مديريت نشده (مانند فايل) که بايد برنامه نويس خود مواظب تخريب صحيح آن ها باشد.تکرار مي کنم که اين يک نظر کاملا شخصي است.

موفق باشيد

Sajjad.Aghapour
جمعه 20 دی 1387, 19:31 عصر
ولی به نظر من نحوه کار GC (پاک کردن مکان اشغال شده توسط یک متغیر) چیزی غیر از آنچیزی است که ما فکر می کنیم.به نظر من این کار شبیه به همون کار defrag ی هست که شما گفتید.
یعنی وقتی یک شی توسط GC برچسب میخوره باید به طور کامل از حافظه پاک بشه و pointer به محل جدید حافظه سالم اشاره کنه.یعنی از usage قبلی کاسته بشه.من دقیقا این رو امتحان کردم...
.....
در برنامه ای توسط یک دکمه 5 فرم ایجاد کردم.usage مقداری افزایش پیدا کرد.تمامی 5 فرم رو بستم ولی از usage چیزی کاسته نشد.دفعه بعدی باز 5 فرم ایجاد کردم.در این بار هیچ تغییر محسوسی در usage ایجاد نشد و از ایجاد فرم ششم به بعد تغییرات در usage شروع شد....
.....
حالا من موندم و این ابهام...

RoostaYeBekr
جمعه 20 دی 1387, 19:50 عصر
سلام

6) StringBuilder متد Dispose ندارد شايد به اين دليل که احتياج نيست (فضاي زيادي را اشغال نمي کند).در هرحال نمي داند چرا! شايد مربوط به همان MaxCapacity باشد!

موفق باشيد
با سلام و تشکر
ببخشید فقط یک مسئله ای :
اگر یک آرگومان رشته ای را برای StringBuilder تعیین کنید و طول رشته کوچک تر از 16 کاراکتر باشد ، ظرفیت آن 16 کاراکتر در نظر گرفته می شود. ولی اگر طول رشته بیش از 16 کاراکتر باشد ، ظرفیت آن اولین توان 2 بزرگتر از تعداد کاراکترهای آرگومان در نظر گرفته می شود.
پس نتیجه اینکه ظرفیت اشغالی توسط خود StringBuilder هم می تواند زیاد شود.

الان اگر برنامه ی فرستاده شده توسط آقای محمد امین را ببینید ، می بینید که ظرفیت به شکل توان 2 زیاد شده.

RoostaYeBekr
جمعه 20 دی 1387, 20:28 عصر
با سلام
:لبخندساده:
بچه ها دو سوال راجع به کامپوننت هایی که Dispose می شوند:
( برنامه ی اینایی که می گم فرستادم )
:خجالت:
اول یک نکته :
وقتی که در پروژه ی ویندوزی ، یک کامپوننت مثل TextBox را Dispose می کنیم ، بعد از عمل Dispose ، کامپوننت TextBox ، حتی از روی صفحه هم به طور کامل ناپدید می شود که این طبیعی و منطقی است.

اما
در پروژه ی وب ، این اتفاق نمی افتد . چون صفحه که به سمت Server می رود ، از نو ساخته می شود.

حالا سوال :
به این برنامه هایی که فرستادم دقت کنید که TextBox در پروژه ی ویندوزی مقدارش هم خالی نشان می دهد که این طبیعی است . اما در پروژه ی وب بعد از Dispose ، مقدارش را باز نشان می دهد که بسیار غیرطبیعی است.


و یک سوال دیگه اینکه : فکر کنید ما فرم ویندوزی یا صفحه ی وب ما ، چند مرحله دارد . مثلا تا مرحله ی 3 ، کامپوننت TextBox ما ، ویزیبلش False بوده . در مرحله ی 4 باید True شود. آیا این خوب است که ما بعد از تمام شدن استفاده مان از این کامپوننت TextBox ، به جای اینکه ویزیبلش را دوباره False کنیم ، Dispose اش کنیم ؟

Amir Oveisi
جمعه 20 دی 1387, 20:47 عصر
در برنامه ای توسط یک دکمه 5 فرم ایجاد کردم.usage مقداری افزایش پیدا کرد.تمامی 5 فرم رو بستم ولی از usage چیزی کاسته نشد.دفعه بعدی باز 5 فرم ایجاد کردم.در این بار هیچ تغییر محسوسی در usage ایجاد نشد و از ایجاد فرم ششم به بعد تغییرات در usage شروع شد
به این دلیله که معلوم نیست کی توسطGCپاک میشه (و مطئنا خیلی کم نیست این زمان)
ولی وقتی دوباره 5 فرو باز میشن چرا مقدار حافظه مصرفی زیاد نمیشه؟
چون قبلا GC اون قسمت هایی که قلا با 5 فرم اشغال شده بود رو نشون کرده و اونارو دوباره مرتب میکنه تا جای کمتری بگیرن به همی دلیل موقع ایجاد فرم های جدید افزایش چندانی در حافظه مشاهده نمیشه (به دلیل همون defrag که انجام میشه در اون لحظه)


اگر یک آرگومان رشته ای را برای StringBuilder تعیین کنید و طول رشته کوچک تر از 16 کاراکتر باشد ، ظرفیت آن 16 کاراکتر در نظر گرفته می شود. ولی اگر طول رشته بیش از 16 کاراکتر باشد ، ظرفیت آن اولین توان 2 بزرگتر از تعداد کاراکترهای آرگومان در نظر گرفته می شود.
پس نتیجه اینکه ظرفیت اشغالی توسط خود StringBuilder هم می تواند زیاد شود.
stringBuilder طرز کارش به این شکله که یه ظرفیتی براش تعیین میشه و اگه خود برنامه نویش مقدارش رو تعیین نکنه به مقدار پیش فرض ست میشه
حالا نکته اینجاست که اگر طول رشته مورد نظر بیشتر از ظرفیت باشه یه کپی از خودش با ظرفیت مناسب ایجاد میکنه و اون قبلیرو میده دست GC
پس مهمترین نکته تو کار با StringBuilder تعیین ظرفیت مناسبه

در ضمن میزان مصرف حافظه هر برنامه ای توسط TaskManager قابل رویته و برنامه های ما نیز از این اصل مستثنی نیستند.


موفق باشید
jooje

RoostaYeBekr
جمعه 20 دی 1387, 21:54 عصر
با سلام
یک برنامه راجع به GC نوشتم که البته بیشتر منظورم این بود که خودتان BreakPoint بگذارید و ترتیب اینکه اول Destructor اجرا می شود یا GC ببینید.
:لبخندساده:
ولی در هر صورت :
قبل از اینکه GC ، حافظه اشیا را بازیابی کند ، Destructor را فراخوانی می کند تا کارهای اختتامیه را روی آن شیء انجام دهد. اما نمی توان دقیقا مشخص کرد که مخرب کی فراخوانی می شود ، زیرا همانطور که قبلا هم گفته شد، نمی توانیم دقیقا مشخص کنیم که کی GC فراخوانی می شود.

محمدامین شریفی
جمعه 20 دی 1387, 23:46 عصر
حالا سوال :
به این برنامه هایی که فرستادم دقت کنید که TextBox در پروژه ی ویندوزی مقدارش هم خالی نشان می دهد که این طبیعی است . اما در پروژه ی وب بعد از Dispose ، مقدارش را باز نشان می دهد که بسیار غیرطبیعی است.

فکر کنم در این باره خودتان یک تاپیک زدید،یادتان هست که درباره enableviewstate بود و کلی هم بحث شد.اگر لینکش را یادتان هست بنویسید.


و یک سوال دیگه اینکه : فکر کنید ما فرم ویندوزی یا صفحه ی وب ما ، چند مرحله دارد . مثلا تا مرحله ی 3 ، کامپوننت TextBox ما ، ویزیبلش False بوده . در مرحله ی 4 باید True شود. آیا این خوب است که ما بعد از تمام شدن استفاده مان از این کامپوننت TextBox ، به جای اینکه ویزیبلش را دوباره False کنیم ، Dispose اش کنیم ؟
به نظر من مخفیش کنیم بهتر است.

Sajjad.Aghapour
شنبه 21 دی 1387, 00:38 صبح
به این دلیله که معلوم نیست کی توسطGCپاک میشه (و مطئنا خیلی کم نیست این زمان)
خوب این وقتی است که کنترل GC بر عهده ما نباشه.ولی طبیعتا با frm.Dispose و GC.Collect این شی باید از حافظه پاک بشه.چون در این مرحله که فرم بسته شده حتی بدونfrm.dispose این شی یک گزینه برای GC هست و با GC.Collect باید از حافظه پاک بشه در حالیکه در ظواهر چیز دیگه ای نشون داده میشه.....


ولی وقتی دوباره 5 فرو باز میشن چرا مقدار حافظه مصرفی زیاد نمیشه؟
چون قبلا GC اون قسمت هایی که قلا با 5 فرم اشغال شده بود رو نشون کرده و اونارو دوباره مرتب میکنه تا جای کمتری بگیرن به همی دلیل موقع ایجاد فرم های جدید افزایش چندانی در حافظه مشاهده نمیشه (به دلیل همون defrag که انجام میشه در اون لحظه)
بله این یک امر کاملا طبیعی هست.ولی این موضوع برای وقتی سودمند هست که یک شی برای دفعات بیشتر از یک بار یا بصورت مکرر استفاده بشه.
ولی شما فرض کنید یک فرم login که دارای یک کلاس برای خواندن username password از db هست.خوب اینو چه کارش کنیم.....
آیا باید در حافظه بمونه تا وقتی GC فراخوانی بشه....
البته اکثرا ذکر میشه که کنترل GC به ندرت به عهده برنامه نویس خواهد بود.ولی به نظر من این مورد یکی از مواردی است که برنامه نویس باید یه فکری براش بکنه......

RoostaYeBekr
شنبه 21 دی 1387, 11:10 صبح
فکر کنم در این باره خودتان یک تاپیک زدید،یادتان هست که درباره enableviewstate بود و کلی هم بحث شد.اگر لینکش را یادتان هست بنویسید.

.
نه نه نه . این رو با اون اشتباه نگیرید. بحث من تو اون تاپیک بعد از PostBack بود. اما اگر دقت کنید درهمون برنامه ی وب ، یعنی کد زیر :


protected void Button_Dispose_TextBox_Click(object sender, EventArgs e)
{
TextBox1.Dispose();
TextBox2.Text = TextBox1.Text;
}

بعد از Dispose شدن TextBox1 که PostBack ی صورت نمی گیرد ، مگر اینکه خط کد زیر را هم اجرا کند :


TextBox2.Text = TextBox1.Text;

اگر من این خط کد بالا رو در قسمت Load صفحه نوشته بودم ، حرف شما صحیح بود. ولی مسئله این است که از خط کد Dispose تا خط کد بالا ، هیچ فاصله ای نیست.

babakj
شنبه 21 دی 1387, 13:06 عصر
مرسی ولی جواب سوال من اینها نبود

یک کلاس داریم که مشخصات یک دانش آموز رو وارد بانک یا database میکنه - این کلاس رو خودمون نوشتیم .... تا اینجا درست؟
ازروی اون یک instance می سازیم , یعنی لری بگم new میکنیم
بعد کارمون باهاش تموم می شه چه کارش می کنیم ؟ nothing می کنیم بعدش dispose میکنیم
یا به امون خدا ولش می کنیم


بابا سئوال من اینهههههه ؟؟؟ باید dispose رو توی بدنه اون کلاس پیاده سازی کنیم یا نه و ازش استفاده کنیم ؟

غیر از مطالب خوب آقای SMRAH1 (http://barnamenevis.org/forum/member.php?u=44567) من جواب قانع کننده ایی پیدا نکردم اگر میشه دوباره دوستان این سوال منو مطالعه کنند

محمدامین شریفی
شنبه 21 دی 1387, 13:39 عصر
1.به خدای مهرپرستی همه دارند درباره همین صحبت میکنند!!!مسئله اینست که شما داخل کلاست از چه چیزهایی استفاده کرده اید.خوب اگر شما میخواهید با اصول طراحی سه لایه ای پیش بروی باید Destructor برای کلاست بنویسی.
2.رفیق وقتی شما textbox ایجاد میکنی و کدت را هم کامپایل، در اصل شما چند کنترل بوجود می آوری که hidden هم جزو اصلی اونهاست،hidden هم متن textbox را همیشه در خود نگه میدارد.ربطی به ispostback شدن یا نشدن ندارد،چون شما قبلا آنرا ایجاد کرده اید.یعنی بیرون از آن 2 خط کدتان.درست است؟
3.سجاد جون بستگی دارد شما کد ADO ات را چگونه بنویسی،دوستان گفتند اگر از یک data یا IO استفاده کردی اید آنرا ببندید(یعنی دسترسی خود را از آن نابود کنید)،و کدهایی را که با new ساخته اید خود GC از بین میبرد.
دوستان اگر اشتباه نوشتم ببخشید،لطفا صحیحش را بگویید.

با سپاس

Amir Oveisi
شنبه 21 دی 1387, 14:38 عصر
بله این یک امر کاملا طبیعی هست.ولی این موضوع برای وقتی سودمند هست که یک شی برای دفعات بیشتر از یک بار یا بصورت مکرر استفاده بشه.
ولی شما فرض کنید یک فرم login که دارای یک کلاس برای خواندن username password از db هست.خوب اینو چه کارش کنیم.....
آیا باید در حافظه بمونه تا وقتی GC فراخوانی بشه....

اینطوری نیست که هر object که تو حافظه منتظر GC هست فقط دوباره توسط همون object جایگزین بشه، بلکه هر چیز دیگه ای بخواد load بشه تو حافظه اون قبلیا که منتظرن defrag میشن.
مثلا ممکنه یه فرم جدیدی که باز میکنید بره جای فرم login یا اینکه اصلا یه آرایه بره جاش!


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

RoostaYeBekr
شنبه 21 دی 1387, 16:31 عصر
1.2.رفیق وقتی شما textbox ایجاد میکنی و کدت را هم کامپایل، در اصل شما چند کنترل بوجود می آوری که hidden هم جزو اصلی اونهاست،hidden هم متن textbox را همیشه در خود نگه میدارد.ربطی به ispostback شدن یا نشدن ندارد،چون شما قبلا آنرا ایجاد کرده اید.یعنی بیرون از آن 2 خط کدتان.درست است؟


با سلام و تشکر
ببین دستت درد نکنه . ولی راستش باور نکردم . آخه می دونی چرا:

شما اصلا فکر کن به جای کد زیر :


protected void Button_Dispose_TextBox_Click(object sender, EventArgs e)
{
TextBox1.Dispose();
TextBox2.Text = TextBox1.Text;
}


نوشته ام :


protected void Button_Dispose_TextBox_Click(object sender, EventArgs e)
{
//TextBox1.Dispose();
TextBox1.Text = "";
TextBox2.Text = TextBox1.Text;
}

خوب . چطور تو حالت اول ، مقدار TextBox1 را می رود از Input اش می خواند ، ولی تو حالت دوم اینکار را نمی کند؟
ببین به نظرم جواب این سوال ، این نیست که من در حالت دوم نوشته ام :


TextBox1.Text = "";

چون اون وقت من هم می گم که در حالت اول باید کد :


TextBox1.Dispose();

کار خودش رو می کرد.