PDA

View Full Version : سوال در مورد Garbage Collection



Sina.iRoid
چهارشنبه 17 تیر 1394, 11:05 صبح
سلام
لطفا به متد زیر دقت کنید:


public void printVolume() {
double volume = getVolume();
String text = "Volume is: " + volume;
System.out.println(text);
}


طبق مطلبی که من در مورد Garbage Collection خوندم، متغیر text بعد از اجرای این متد از حافظه سیستم پاک میشه. چون داخل بدنه متد تعریف شده و خارج از اون قابل دسترسی نیست. خب اگر ما دوباره این متد و اجرا کنیم، دوباره متغیر text در حافظه ساخته میشه، اما اگر ما مقداری در اون ذخیره کرده باشیم از بین رفته؟ درسته؟ خب چرا باید این اتفاق بیفته!؟ شاید ما تا آخر برنامه به این متغیر نیاز داشته باشیم؟
ممنون میشم اگر راهنماییم کنید :)

azimi.moja
چهارشنبه 17 تیر 1394, 13:41 عصر
سلام دوست عزیز.
این قابلیت زبان هستش. متغیر محلی برای همین تعریف میشه که جای دیگه نیازی به استفاده ازش نیست. شما اگر می خوای ئداده مورد نیاز رو نگه داری باید متغیری خارج از متد بسازی. کمی در مورد قواعد دسترسی متغیرها مطالعه کنی سریعا دستت میاد موضوع.

Sina.iRoid
پنج شنبه 18 تیر 1394, 22:56 عصر
سلام دوست عزیز.
این قابلیت زبان هستش. متغیر محلی برای همین تعریف میشه که جای دیگه نیازی به استفاده ازش نیست. شما اگر می خوای ئداده مورد نیاز رو نگه داری باید متغیری خارج از متد بسازی. کمی در مورد قواعد دسترسی متغیرها مطالعه کنی سریعا دستت میاد موضوع.

سلام
Garbage Collection یکی از ویژگی های جاواست که زبانی مثل سی پلاس پلاس فاقد این ویژگی هست و پاک کردن آبجکت ها از حافظه بر عهده خوده برنامه نویس هست. اما اگر برنامه ای و که در بالا نوشتم و هم توسط ++C بنویسید، باز هم خارج از بلاک تعریف شده ما نمی تونیم به اون متغیر دسترسی پیدا کنیم. به این ترتیب سی پلاس پلاس هم Garbage Collection باید داشته باشه دیگه؟ نه؟ در این مورد من نمی دونم.

azimi.moja
جمعه 19 تیر 1394, 11:22 صبح
دوست عزیز شما چرا مسائل رو قاطی می کنی. این مورد که شما نوشتی به حوزه ی حیات یک متغیر ربط داره, چون بحث دسترسی هست نه وجود داشتن در حافظه. در C شما حوزه ی دسترسی داری که قوانین خاص خودش هست ولی C میگه اگر حافظه ای را اشغال کردی تا برنامه در حال اجراست من اون حافظه رو آزاد نمی کنم مگر خود برنامه نویس این کار رو انجام بده. اینکه در حافظه هست دلیلی به حوزه دسترسی نیست. حالا در جاوا همین حوزه دسترسی ها هست که شباهت زیادی به C داره ولی تفاوت در آزاد سازی حافظه هست. جاوا میگه اگر JRE به این نتیجه برسه حافظه ای استفاده نمیشه (حالا چه محلی چه غیر محلی) به صورت خودکار حافظه را آزاد می کند نه مثل C که برنامه نویس این کار رو باید بکنه.
البته ساختار بررسی و آزاد سازی حافظه که جاوا هست اصلا چیز ساده ای نیست و بسیار پیجیدست. به راحتی هم نمیشه گفت الان که دیگه ای متغیر استفاده نمیشه یا اصلا محلی بوده الات در حافظه هست یا خیر. یعنی هیچ تضمینی برای تضمین وجود یک متغیر بعد از استفاده از آن وجود ندارد. هیچ روش مطمینی هم برای استفاده دستی برنامه نویس از Garbage Collection وجود ندارد.
اگر سوال دیگه ای هست در خدمتم

Sina.iRoid
جمعه 19 تیر 1394, 12:51 عصر
لطف کنید یه مثال ساده بزنید که نشون بده Garbage Collector آبجکت و حذف می کنه. کلا سی پلاس پلاس خیلی با جاوا فرق داره. کده زیر و نگاه کنید:


#include <iostream>
using namespace std;


int main(){


char ch = 'name';
char ch = 'name';


int num = 2;
int num = 2;


return 0;
}


ببینید با اینکه دو تا متغیر هم نام هم هستند ایراد نمی گیره و برنامه درست اجرا میشه. ولی همچین کاری در جاوا امکان پذیر نیست. البته این ربطی به Garbage Collector نداره ولی در کل می خواستم اینم بگم.

azimi.moja
جمعه 19 تیر 1394, 13:44 عصر
عزیزم شما هنوز سینتکس رو بلد نیستی. کد بالا امکان نداره. اولا char رو string نسبت دادی. بعد هم معرفی محلی دو متغیر امکان نداره که همنام باشن. مگر زبان نوع متغیر براش مهم نباشه. حداقل من ندیدم. شما ساختار ها نشناختی. من هر چی توضیح بدم می خوای بگی اشتباه هستش.
حوزه دسترسی متغیر یک مورد هست و Garbage Collection یک مورد. این ها مفهمشون با هم در ارتباط نیست( البته شاید Garbage Collection از مفهوم حوزه دسترسی متغیر برای پاک سازی حافظه اسنتفاده کنه). توضیحات بالا کامل هستش. سوالتون رو موردی بپرسید. همش از این ور اون ور صحبت نکنید. دقیق همون رو شرح بدید.

محمد فدوی
جمعه 19 تیر 1394, 15:39 عصر
سلام
Garbage Collection یکی از ویژگی های جاواست که زبانی مثل سی پلاس پلاس فاقد این ویژگی هست و پاک کردن آبجکت ها از حافظه بر عهده خوده برنامه نویس هست. اما اگر برنامه ای و که در بالا نوشتم و هم توسط ++C بنویسید، باز هم خارج از بلاک تعریف شده ما نمی تونیم به اون متغیر دسترسی پیدا کنیم. به این ترتیب سی پلاس پلاس هم Garbage Collection باید داشته باشه دیگه؟ نه؟ در این مورد من نمی دونم.

سلام. اولین نکته‌ای که توی حرفاتون دیدم اینه که متغیر text بلافاصله بعد از اتمام بلوک کد باید توسط سیستم جمع‌آوری زباله‌ی جاوا یا همون GC حذف بشه. نه اینجوری نیست. اینکه چه زمانی GC می‌آد و اشیاء بدون ارجاع یا همون Garbageها رو پاک می‌کنه قواعد خاص خودش رو داره و توی چندلایه هم انجام می‌شه. اما در این حد بدون که با اتمام هر بلوک کد این‌کار انجام نمی‌شه چون خیلی کار پرهزینه‌ای برای ماشین مجازی جاواست.

نکته‌ی دوم در مورد قیاس اشتباهت بین سی‌پلاس‌پلاس و جاواست؛ سی‌ و سی‌پلاس‌پلاس بصورت پیشفرض از تخصیص حافظه‌ی Static استفاده می‌کنن. یعنی محتویات، نوع و سایز اشیاء در زمان ترجمه تعیین می‌شن. واسه همین بدون هیچ مشکلی بعد از اتمام بلوک کد حافظه‌ی تخصیص یافته بهشون آزاد می‌شه. اما زبان‌های سطح بالاتری مثل جاوا (و زبان‌های فریمورک دات‌نت و غیره...) همواره از تخصیص حافظه‌ی پویا یا Dynamic استفاده می‌کنن. خصوصیت مثبت این نوع تخصیص اینه که بصورت Runtime انجام می‌شه و دست برنامه‌نویس رو خیلی بازتر می‌کنه، اما یه بدی داره و اون اینه که خودبخود از حافظه پاک نمی‌شه... این یعنی برنامه‌نویس مجبوره خودش دستی اشیاء به درد نخور رو پاک کنه. مثلا توی سی‌پلاس‌پلاس اگه با تخصیص حافظه‌ی Static یه آرایه بسازیم:

#define LENGTH 16

int staticArray[LENGTH];
// ...

بعد از اتمام کارمون با staticArray می‌تونیم مطمئن باشیم به موقع از حافظه پاک خواهد شد. در حالی‌که اگه از تخصیص حافظه‌ی پویا استفاده کنیم:

int length;
std::cin >> length;

int* dynamicArray = new int[length];
if(dynamicArray == NULL) {
std::cout << "Oh! Memory is full :(";
// Stop the Program!
}
// ...
delete[] dynamicArray; // Delete it!

خوبیش اینه که می‌تونیم تعداد المنت‌های درون آرایه رو از کاربر دریافت کنیم (در واقع طول آرایه رو بصورت Runtime تعیین کنیم)؛ این کاریه که توی تخصیص حافظه‌ی Static نمی‌تونستیم انجام بدیم (و می‌بینید که اونجا تعداد المنت‌های آرایه رو بصورت Compile-time تعیین کردیم) اما به جاش این محدودیت رو داریم که مجبوریم خودمون هم از حافظه پاکش کنیم! محدودیت دیگه‌ای که تخصیص حافظه‌ی پویا برامون ایجاد کرده اینه که اگه حافظه‌ی کافی برای ساخت dynamicArray وجود نداشته باشه هیچ خطایی رخ نمی‌ده و مدیریت این مشکل هم به عهده‌ی خود ماست (البته در این مورد بین کامپایلر‌های مختلف سی‌پلاس‌پلاس اختلاف هست). بحث از جاوا خارج شد...

اما فریمورک‌های جدیدتر به‌خاطر محدودیت‌های تخصیص حافظه‌ی Static ترجیح دادن که کلا به تخصیص حافظه‌ی Dynamic مهاجرت کنن و برای حل محدودیت تخصیص حافظه‌ی Dynamic، سیستم‌های نیمه هوشمندی به نام سیستم‌های جمع‌آوری زباله رو ساختن که کار پاک کردن Garbageها رو بصورت خودکار انجام می‌ده.

برگردیم سر سؤال اصلیت:‌ «اگه نخواستم text از حافظه پاک شه چکار کنم؟»
درواقع دوحالت وجود داره:
۱. یا همیشه متغیر text قراره یه مقدار ثابت رو نگه‌داری کنه: خب در اینصورت ایده اینه که text رو بصورت final توی سطح بالاتری از متد printVolume (یعنی به عنوان یه متغیر عضو کلاس که حتی ممکنه static هم باشه) تعریف کن! هرچند اگه بصورت final ولی توی متد هم تعریف کنی در آینده JIT می‌فهمه که این متغیر همیشه مقدار ثابتی داره و بصورت هوشمند دیگه از اول نمی‌سازتش.

۲. یا هربار که متد printVolume فراخوانی می‌شه ممکنه محتویات text تغییر کنه (که اینطور به نظر می‌رسه): در این حالت راهی نیست جز اینکه هربار text از اول ساخته بشه. چون احتمال این هست که مقدار جدیدی رو توی خودش داشته باشه.


#include <iostream>
using namespace std;


int main(){


char ch = 'name';
char ch = 'name';


int num = 2;
int num = 2;


return 0;
}
اولا اینکه متن برنامه‌تون کاملا اشتباهه (رشته‌ها از نوع آرایه‌ای از charها هستن و نه خود char و سی‌پلاس‌پلاس از نقل قول دوتایی برای رشته‌ها استفاده می‌کنه):

#include <iostream>
using namespace std;

int main() {
char name1[] = "name";
char name2[] = "name";

int num1 = 2;
int num2 = 2;

return 0;
}

و درضمن در سی و سی‌پلاس‌پلاس هم مثل جاوا ساخت دوتا متغیر با یک نام اینجا هم غیرمجازه. به جز درشرایطی که اون دوتا متغیر مربوط به دوتا حوزه‌ی تعریف قابل تفکیک باشن...

موفق باشید.

Sina.iRoid
جمعه 19 تیر 1394, 23:13 عصر
سلام
آقای محمد فدوی من کتابی در مورد برنامه نویسی جاوا دارم که در بخش معرفی Garbage Collection دقیقا مثالی که من ابتدا نوشتم (پاک شدن text) و نوشته. و گفته که این متغییر توسط GC پاک شده. و این نکته رو هم گفته که زبان هایی مثه ++C فاقد این قابلیت هستند و پاک کردن آبجکت ها بر عهده خوه برنامه نویس هست. این نکته باعث شد که من بیام جاوا رو با ++C مقایسه کنم. در مورد سی++ ادعایی ندارم. چون دیدی که از جاوا داشتم و در سی ++ پیاده کردم و اون برنامه رو اصلا اجرا نکردم. چون فک کردم ویژوال استدیو مثه اکلیپس همون لحظه که کد و می نویسی اگه اشکالات این مدلی داشته باشه رو بت میگه که اینطور نیست. حتما باید برنامه رو اجرا کنی. بر گردیم سره بحث اصلی.


جناب قدوی فقط من یه نکته ای و نمی دونم من متوجه نشدم یا اینکه همین و شما فرمودین. یکم گیج شدم. در ابتدا گفتم که GC مخصوص جاواست و سی پلاس پلاس نداره. این حرف درسته؟ در مثالی که شما فرمودین بعد از تمام شدن کارمون با حافظه استاتیک خودش پاک میشه که در مورد داینامیک ها این طور نیست؟ درسته؟ خب پس جاوا داینامیک هست و ما خودمون باید پاک کنیم. در این صورت Garbage Collector چی میشه؟ شاید سوال خیلی ابتدایی باشه ولی واقعا نفهمیدم.
ممنون

محمد فدوی
دوشنبه 22 تیر 1394, 01:07 صبح
و گفته که این متغییر توسط GC پاک شده.
این حرف رو احتمالا نگفته و اگرم گفته اشتباه گفته. درسته متغیر text بعد از اینکه بلوکش تموم می‌شه دیگه از دسترس برنامه‌نویس خارج می‌شه و احتمالا به زودی هم توسط GC از حافظه پاک خواهد شد. فرآیند جمع زباله رو توی جاوا می‌شه بصورت دستی با فراخوانی متد System.gc جلو انداخت تا سریعا انجام شه... ولی الزامی وجود نداره که GC دقیقا بعد از اتمام بلوک کد بصورت خودکار وظیفه‌ش رو انجام بده.



در ابتدا گفتم که GC مخصوص جاواست و سی پلاس پلاس نداره. این حرف درسته؟

بله.



در مثالی که شما فرمودین بعد از تمام شدن کارمون با حافظه استاتیک خودش پاک میشه که در مورد داینامیک ها این طور نیست؟ درسته؟

بله.



خب پس جاوا داینامیک هست و ما خودمون باید پاک کنیم. در این صورت Garbage Collector چی میشه؟

کار GC اینه که وظیفه‌ی پاک کردن اشیاء به درد نخور رو بصورت نیمه هوشمند از ما تحویل می‌گیره و ما رو از اینکار راحت می‌کنه! مثلا وقتی که شما توی یه بلوک یه شیء رو به یه متد پاس می‌کنی ممکنه حیات اون شی‌ء خیلی خیلی طولانی‌تر بشه (چون هنوز بش نیاز هست...)، یه مثال بزنیم:

class Phone {
private final String modelName;
public Phone(String modelName) { this.modelName = modelName; }
}

public class GarbageCollectorTest {
public static void main(String[] args) {

Phone myPhone;
// Inner Block
{
String temp = "11-00";
myPhone = new Phone(temp);
}
}
}

الان انتظار داریم بعد از پایان بلوک داخلی (Inner Block) بعد از مدتی temp از حافظه پاک بشه. در صورتی که باتوجه به اینکه به شیء temp توی myPhone (به‌عنوان نام مدل) نیازه، GC بصورت هوشمند این رو می‌فهمه و temp رو فعلا پاک نمی‌کنه.
در صورتی که اگر مثلا توی سی‌پلاس‌پلاس بودیم وظیفه‌ی پاک کردن اشیاء پویا از حافظه به‌عهده‌ی ما بود و اینجور تشخیص‌ها رو هم باید خودمون می‌دادیم!

-سیّد-
سه شنبه 30 تیر 1394, 21:53 عصر
ضمن تشکر از آقای فدوی بابت توضیحات عالیشون، یه نکته‌ی کوچیک رو تصحیح می‌کنم:


اما زبان‌های سطح بالاتری مثل جاوا (و زبان‌های فریمورک دات‌نت و غیره...) همواره از تخصیص حافظه‌ی پویا یا Dynamic استفاده می‌کنن.

من خیلی درباره‌ی دات‌نت اطلاعات خوبی ندارم، اما در مورد جاوا می‌دونم که وقتی از primitive ها استفاده می‌کنید، دیگه حافظه به صورت پویا تخصیص داده نمی‌شه و کار داخل Stack انجام می‌شه (البته استفاده از Stack هم یه جورایی پویا هست، ولی با استفاده از Heap خیلی فرق می‌کنه). این قضیه یه مقدار دقیق هست. توی این صفحه بیشتر درباره‌اش توضیح دادم:
http://blog.yooz.ir/?q=node/23

نکته‌ی GC همونطور که گفتن، اینه که کار شما رو راحت کرده. یعنی توی زبانی مثل ++C شما هم می‌تونید اشیاء رو داخل Stack تعریف کنید (به صورت ایستا) که در این صورت بعد از خروج از بلوک کد، خود به خود از حافظه پاک خواهد شد (این خاصیت Stack هست)، هم می‌تونید داخل Heap تعریف کنید (به صورت پویا) که در این صورت وظیفه‌ی پاک کردن اون شیء بر عهده‌ی خود شما هست. یعنی باید حواستون باشه که کی دیگه از اون شیء به هیچ وجه استفاده نخواهید کرد، و به محض این که مطمئن شدید به صورت دستی پاکش کنید. اینجا ۲ تا نکته هست:
یکی این که نباید یه شیء رو قبل از این که استفاده‌اش تموم بشه پاک کنید، که اگه این کارو بکنید برنامه‌تون با مشکل برخورد خواهد کرد.
دوم این که نباید یه شیء رو بعد از این که استفاده‌اش تموم شد نگه دارید و پاکش نکنید، که اگه پاکش نکنید، برنامه‌تون دچار Memory leak می‌شه و بعد از یه مدت اجرای برنامه‌تون، حافظه‌ی heap پر می‌شه و برنامه‌تون می‌ترکه!

خوب حالا کافیه یه مقدار برنامه‌تون بزرگ و پیچیده بشه، تا سردرد بگیرید که یه شیء رو کی پاکش کنم و کی پاکش نکنم! با توجه به این قضیه، این ایده به وجود اومد که برنامه‌نویس به جای این جور چیزای جانبی که ربطی به منطق برنامه و الگوریتم‌های اون نداره، باید تمام حواسش و توانش رو جمع کنه تا منطق برنامه درست باشه و باگ نداشته باشه. برای همین ایده‌ی Garbage Collector به وجود اومد که به صورت خودکار اشیائی که دیگه به درد نمی‌خورن رو تشخیص بده و از حافظه پاک کنه.
توی جاوا همین کارو کردن. یعنی شما هر وقت به یه شیء نیاز داشتی، اون رو بساز (که قطعاً هم توی Heap ساخته خواهد شد و امکان ساختن یه شیء داخل Stack در زبان جاوا نیست) و بعدش دیگه کاری نداشته باش! JVM خودش حواسش هست که هر وقت دیگه نیازی به اون شیء نبود پاکش کنه. پس توی جاوا دستور new داریم، ولی دستور delete یا free نداریم.

البته اینجا بحث خیلی زیاده، مثلاً این که می‌شه یه مقدار رفتار GC رو کنترل کرد، یا این که می‌شه کد رو یه جوری نوشت که رفتارش متناسب با GC باشه و خیلی اذیتش نکنه، یا این که GC انقدرا هم که خط بالا گفتم رؤیایی کار نمی‌کنه و بعضی وقتها یه مقدار کار رو خراب می‌کنه (مثلاً ممکنه اجرای یه دوره‌ی GC خییییییییییییلی (در حد چندین ثانیه یا حتی دقیقه) طول بکشه و باعث بشه برنامه‌تون کاراییش رو از دست بده. مخصوصاً اگه برنامه نیاز به sync کردن خودش با یه سیستم دیگه داشته باشه، یا نیاز به فرستادن heartbeat های دائمی به یه سیستم دیگه داشته باشه تا نشون بده که زنده هست، ممکنه یه GC طولانی باعث بشه که بین ۲ تا heartbeat خیلی فاصله بیافته و در نتیجه سیستم از دید اون یکی سیستم دیگه مرده به نظر برسه. این قضیه مخصوصاً توی سیستم‌های توزیع‌شده خیلی مهم هست)، یا این که یه سری JVM ها هستن که GC رو به صورت دوره‌ای انجام نمی‌دن و دائم مواظب سیستم هستن و هر شیئی به محض این که garbage بشه جمعش می‌کنن (مثل Zing JVM (http://www.azulsystems.com/products/zing/whatisit) که اصلاً برای حافظه‌های خیلی بزرگ (دهها گیگابایت) طراجی شده).

یه نکته‌ی دیگه این که توی ++C می‌تونید یه primitive رو هم داخل heap تعریف کنید: این کار رو می‌تونید با استفاده از اشاره‌گرها انجام بدید. اما توی جاوا primitive ها فقط می‌تونن توی Stack باشن.

پس خلاصه‌ی بحث شد این:



Language
primitives
Objects
delete


C++‎‎‎‎
Stack or Heap
Stack or Heap
manual


Java
Stack
Heap
automatic




البته همونطور که گفتن توی ++C هم فریم‌ورک‌هایی برای انجام نیمه‌خودکار GC به وجود اومده که دیگه بحث رو خیلی طولانی می‌کنه.

ebiramgs
جمعه 01 آبان 1394, 21:38 عصر
سلام
لطفا بگین از چه روشی میشه مدت زمانی که طول میکشه تا garbage collector یه متغییر رو از داخل heap پاک کنه رو پیدا کرد؟

-سیّد-
یک شنبه 03 آبان 1394, 01:01 صبح
سلام
اول: می‌خواین این زمان رو بدونین که چی بشه؟! :لبخند:
منظورم اینه که کاربردتون براش چیه؟ چون خیلی معمول نیست. مگر این که برای تست کردن بخواین این کارو بکنین.

دوم: خوب حالا اگه برای تست کردن می‌خواین، می‌تونین از تابع finalize کمک بگیرین. توی این تاپیک یه مقدار درباره‌ی این تابع توضیح دادم:
http://barnamenevis.org/showthread.php?489039-JVM-%D9%88-%D8%AA%D9%81%D8%A7%D9%88%D8%AA-%D9%88%D8%B8%DB%8C%D9%81%D9%87-%D8%A2%D9%86-%D8%A8%D8%A7-%DA%A9%D8%A7%D9%85%D9%BE%D8%A7%DB%8C%D9%84%D8%B1&p=2211781&viewfull=1#post2211781
می‌تونید لحظه‌ای که شیء رو رها می‌کنید زمان رو داخل شیء ذخیره کنید، و توی تابع finalize حساب کنید که از اون لحظه چقدر گذشت تا تابع finalize شما اجرا بشه:

public class Test {
private long releaseTime = -1;

public void release() {
releaseTime = System.currentTimeMillis();
}

@Override
protected void finalize() throws Throwable {
System.out.println("finalize() called in " + (System.currentTimeMillis() - releaseTime) + "ms");
}

public static void main(String[] args) throws InterruptedException {
Test test = new Test();
test.release();
test = null;
for (int i = 0; i < 2000000; i++) {
String s = String.valueOf(System.currentTimeMillis());
String[] splitted = s.split("\\t\\s");
if (splitted.length > 2)
System.out.println("OMG!");
}
}
}

توی اون حلقه‌ی آخر main کلی شیء الکی می‌سازیم و بلافاصله دور می‌ریزیم تا Garbage collector کارش رو شروع کنه.
البته با این کار می‌تونید بفهمید که شیء مورد نظر چقدر طول کشید تا سیستم finalize اش رو فراخوانی کرد. ولی برای یه سیستم معمولی که Finalizer توش خیلی درگیر نیست، زمان فراخوانی finalize فاصله‌ی خیییییلی کمی با زمان تشخیص garbage داره.