الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

برنامجي يستنزف الذاكرة بشكل كبير ..

مغلق
بدأه Abdullah.Alshammeri في 12 أبريل 2007 · 14 رد · 1,084 مشاهدة · في لغة C و ++C
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم .

أواجه حاليا في برامجي " العادية" استزاف شديد للذاكرة .(أتوقع هذا ما يعرف بالMemory leaks . . وبشكل شنيع .)

لدرجة انه " لعبة صغيرة " تستنزف حوالي 200 ميقا بايت .. مع مرور الوقت .

حيث عندما تبدأ اللعبة .. تكون المساحة المحجوزة من الذاكرة ( ا ميقا تقريبا) .. ومع مرور الوقت وأنا قاعد اشغل اللعبة واراقب استهلاك الذاكرة من خلال " مدير المهام" التابع للويندوز .. الاحظ انه في كل LOOP.. تزداد الذاكرة المحجوزة لبرنامجي بمقدار 300 كيلو بايت ( وأحيانا تكون الزيادة عشوائية) .

قمت بتتبع البرنامج .. وحذف والغاء متغيرات واكتشفت التالي " يعني عرفت المشكلة لكن بقي الحل " :

المشكلة :

اللعبة تستخدم حلقة تكرارية while .

في كل loop .. استدعي دالة تقوم بحجز مؤشر ..

سأضع الاكواد باختصار :

RECT* get( )
{
	RECT  *r=new RECT();
	r->left=r->right=r->top=r->bottom=12;
	return  r;
} 
int main()
{

	 while(1)
	 {

		 get( );
	 }
 	return 0;
}

البرنامج يستنزف الذاكرة بشكل رهيب ..

أتوقع المشكلة ( والله اعلم) بسبب اني استدعي الدالة get التي تقوم بحجز مؤشر .. وتعمله new . في كل loop .

أنا اللي اعرفه .. انه عندما احجز مؤشر من نفس الاسم ( يتم استبداله بمكان المؤشر السابق )

لكن الظاهر انه المكان اللي يؤشر عليه يبقى محجوز .

لو قمت بتغيير الكود السابق الى هذا :

RECT* get( )
{
	RECT   r;
	r.left=r.right=r.top=r.bottom=12;
	return  &r;
} 
int main()
{

	 while(1)
	 {

		 get( );
	 }
 	return 0;
}

لبقيت الذاكرة المحجوزة ثابتة . .

سؤالي :

* أنا احتاج لحجز مؤشر داخل function . . وفي كل loop اطلب من الدالة ان تعيد لي المؤشر .

ولكن هذا يستنزف الذاكرة بشكل رهيب .

المشكلة مع الدوال .. اني لما اعمل return للعنوان .. خلاص .. مافيه امكانية لعمل delete للمؤشر من داخل الدالة

* هل يوجد برنامج أو طريقة . . لمعرفة اسم المتغير الذي قام بحجز الذاكرة اثناء التشغيل .. حتى استطيع الوصول اليه بسرعة بدلا من التجارب العشوائية.. واقوم بحل المشكلة .

عندي اسئلة اخرى .. لكن نؤجلها بعدين .

ماهو الحل يا اخوان !

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#2

انا مدري طريقة عملك او وش الفائدة

لكن ليش ما تسوي مصفوفة من مؤشرات وكل ما شفت ان الـ array مليانه تحذف الشي الي ما تبيه ممكن صح كذا تحجر عدد المتغيرات يعني اكيد ما راح تحتاج كل هالمؤشرات والي فهمته من كلامك المشكلة وين تسوي ديليت :)

#3

وعليكم السلام ورحمة الله

من فترة ما مرة علينا مواضيع للنقاش مثل هذا الموضوع , أغلب المواضيع حل واجبات ومشاريع فالله يعين .

أخوي الشمري

المتغيرات من حيث مكان حجزها نوعين : نوع يخزن في الstack والنوع الثاني يخزن في heap.

المتغيرات المحجوزة في الStack:

هي اللي يتم حجزها داخل الدوال والBlock وغيرها أو بالأحرى هي يسمونها الLocal Variable وهذه من خصائصها أنها يتم تدميرها بمجرد الخروج

من الBlock المحجوزة فيه . يعني ما ينفع تدخل عليها بالمؤشر من مكان ثاني .

أما المتغيرات المحجوزة في الHeap:

هي التي يتم حجزها عن طريق الدالة new . وحجزها يتم وقت تنفيذ البرنامج . ومن خصائصها أنها تظل في ذاكرة البرنامج حتى يتم حذفها بواسطة delete

نجي للبرنامج اللي كتبته

RECT* get( )
{
	RECT  *r=new RECT();
	r->left=r->right=r->top=r->bottom=12;
	return  r;
}
int main()
{
	 while(1)
	 {
		 get( );
	 }
	return 0;
}

في الدلة get أنت حجزت خانتين في الذاكرة وهي :

المؤشر r : ومكانه في Stack وهذا مافي خوف منه لأنه راح يتم حذفه بمجرد الخروج من الدالة .

الكائن RECT اللي يؤشر عليه r : وهذا مكانه في الheap وهذا الكائن إذا فقدت المؤشر اللي يؤشر عليه راح يكون عندك تسرب في الذاكرة .

RECT* get( )
{
	RECT   r;
	r.left=r.right=r.top=r.bottom=12;
	return  &r;
}
int main()
{
	 while(1)
	 {
		 get( );
	 }
	return 0;
}

هنا أنت حليت مشكلة تسرب الذاكرة بحيث إن الكائن RECT راح يكون مكانه في Stack , لكن وقعت مشكلة ثانية وهي إنك ما عاد تقدر تستخدمه خارج الدالة get لأنه تم تدميره.

الحل :

الحل الأسهل هو أنك تخزن المؤشر بعد ما ترجعه الدالة get وتقوم بحذفه قبل لا تستدعي الدالة get.

وفي أكثر من حل آخر يعتمد على طريقتك في البرمجة وتوزيع الأدوار بين الدوال . يعني تقدر مثلاً تخلي الدالة تستقبل مؤشر من نوع RECT وتصلح له initialize وترجعه وفي المرة الثانية ترسل لها نفس المؤشر , هذا إذا كان المؤشر الأول لم تعد هناك حاجة له .

و .... و ..... و ............. أهم شئ إنك تحدد متى راح ينتهي فائدة هذا المتغير فالبتالي أنت بين خيارين إما حذفه أو إعادة إستخدامه .

أما البرنامج فما أعرف برنامج بصراحة , وأمتنى إني أفدتك :)

#4

استخدم الديستركتر....

#5

الأخ الفاضل ...

بالنسبه للكود الأول، يمكنك أضافة تعديل بسيط بعد الإنتهاء من استخدام المؤشر الذى ترجعه الدالة get ، وهو حفظ هذا المؤشر فى متغير داخلى pr وبعد الإنتهاء من أستخدامه ، حذف الذاكرة بأستخدام delete التى يشير لها هذا المؤشر والمحجوزة بـ new، وهذه الطريقة تكون مفيدة فى حالات كثيرة خاصتاً لو كنت تستخدم هذه الالة فى بداء thread، ويصبح الكود كالتالى:

RECT* get()
{
	RECT	*r=new RECT();

	r->left=r->right=r->top=r->bottom=12;
	return  r;
}
int main()
{
	RECT	*pr;

	while(1)
	{
		pr	= get();
		// ...
		// TODO: your work using [pr] RECT pointer
		//
		delete pr;
	}
	return 0;
}

أما لو فضلت أستخدام الطريقة الثانية ، فيمكنك إجراء تعديل بسيط على الدالة get بحث ترجع قيمة r وليس مؤشر له، ويمكنك بعد ذلك الحصول على المؤشر بالطرق المعروفه واستخدامه ، اذا كنت صممت برنامجك على استخدام المؤشرات ولا ترغب فى تعديل كامل البرنامج، ويكون الكود بالشكل التالى:

RECT get()
{
	RECT	r;

	r.left=r.right=r.top=r.bottom=12;
	return r;
}

int main()
{
	RECT	r, *pr;

	while(1)
	{
		r	= get();
		pr	= &r;
		// ...
		// TODO: your work using [pr] RECT pointer
		//
	}
	return 0;
}

شكراً

Eng. Usama El-Mokadem

Nothing is impossible, the word impossible itself says that: I M - Possible

#6

أعتقد أن هذه هي الصورة الأفضل

RECT	*r;
RECT get()
{
	if(!r)
{
r = new RECT;
}

	r->left=r->right=r->top=r->bottom=12;
}

int main()
{

	while(1)
	{
		get();
		// work with r
	}
delete r;
	return 0;
}
#7

السلام عليكم أخ الشمري ,,

لدي إضافة بسيطة فقط ,, يمكنك حجز مصفوفة مؤشرات global للـ RECT ,, ثم في الوقت المناسب قم بحذف ما تم حجزه في المصفوفة ,, و بالتالي تحذف الذاكرة التي تم حجزها ,, الوقت المناسب هو الوقت الذي تقرر أنك لم تعد تحتاج لما قد تم حجزه :D

تحياتي ,,

#8

بارك الله فيكم جميعا ..

جربت احدى الطرق اللي ذكرتوها .. ونجحت بامتياز /

اقتباس
الحل الأسهل هو أنك تخزن المؤشر بعد ما ترجعه الدالة get وتقوم بحذفه قبل لا تستدعي الدالة get.

RECT	*pr;

	while(1)
	{
		pr	= get();
		// ...
		// TODO: your work using [pr] RECT pointer
		//
		delete pr;
	}

كل ردود الاخوة .. مهمة .. ولكن بعضها لا تناسب البرنامج اللي قاعد اعمل عليه .

فأنا احاول قدر الامكان الابتعاد عن الGlobal Variables ..

وكانت مشكلتي هي متى استدعي الامر delete .

---

انا لدي اسئلة اخرى .. وهي ايضا خاصة بالذاكرة ..

أي برنامج أقوم بتنفيذه .. يستهلك على الاقل 500 كيلو بايت .. من الذاكرة .

حتى لو كان مجرد Print على شاشة الكونسول .

فماهو السبب .. هل هو من المكتبات التي اربطها بشكل ستاتيكي ..

بالرغم من اني لا استخدم الى الIOstream .. ولا اعرف هل يتم ربطها ببرنامجي او لا ,,

يعني مثلا هذا البرنامج

int main()
{

while(1)
{
}



 	return 0;
}

يستهلك 560 كيلو بايت من الذاكرة !!!!!!!!!!

فهل من طريقة لتقليل الذاكرة المحجوزة من الرام . . .

تم تعديل هذه المشاركة بواسطة الشمري في 13 أبريل 2007 في 13:54

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#9

الأخ الفاضل ...

اقتباس
جربت احدى الطرق اللي ذكرتوها .. ونجحت بامتياز

لماذا لم تذكر أى هذه الطرق، كى تعم الفائده على الجميع ولكل من ستصادفه نفس المشكله ويبحث عنها ويجد هذا الموضوع؟؟؟

اقتباس
فأنا احاول قدر الامكان الابتعاد عن الGlobal Variables ..

أود أيضاً أن الفت انتباهك إلى أن الحل الذى ارفقته لك لا يستخدم الـ Global Variables، - وبالطبع الـ Global Variables معروف لدى الجميع - ولكن يبدوا انك لم تنتبه إلى أن تعريف RECT *pr داخل دالة main وليس Global، فأنت لم تنسخ الكود كاملاً، لذا ظهر وكأنه يبدوا Global، فالكود كامل كما فى المشاركة الأصلية هو:

Eng. Usama El-Mokadem كتب:
RECT* get()
{
	RECT	*r=new RECT();

	r->left=r->right=r->top=r->bottom=12;
	return  r;
}
int main()
{
	RECT	*pr;

	while(1)
	{
		pr	= get();
		// ...
		// TODO: your work using [pr] RECT pointer
		//
		delete pr;
	}
	return 0;
}

شكراً

Eng. Usama El-Mokadem

Nothing is impossible, the word impossible itself says that: I M - Possible

#10

يوجد حد أدنا لحجم الملف التنفيذي لجميع الكمبايلرات . وهي تختلف من كمبايلر إلى آخر .

لأن الكمبايلر يقوم بتضمين ملفات أساسية لبرنامجك منها ال startup procedure وغيرها .

لكن في نقطة مهم وهي إنك تتأكد إن ترجمة المشروع من نوع Release وليس Debug . لأن الأخيره تضيف أوامر كثيرة للملف التنفيذي ليسهل عملية تتبع الأخطاء .

عموماً علمنا وش نوع الكمبايلر اللي تستخدمه وراح نشوف وش المشكلة .

وللمعلومية إذا كنت تعاني مشكلة من المؤشرات في برامجك جرب تستخدم الSmart Pointer . إبحث عنها في الإنترنت وراح تحصل أنواع كثيرة وكل وحده لها قوانين مختلفة . وفيه في مكتبة الSTL مؤشر من نفس هذه النوعيه , إستخدمه إذا كنت تحتاجه . ;)

#11

الاخ أسامة .

أنا ذكرت الطريقة التي جربتها في مشاركتي السابقة .

وهي التي ذكرها الاخ بندر وأنت ايضا .. وقد ذكرت ذلك .

اقتباس
جربت احدى الطرق اللي ذكرتوها .. ونجحت بامتياز /

إقتباس ( من مشاركة الزميل بندر )

الحل الأسهل هو أنك تخزن المؤشر بعد ما ترجعه الدالة get وتقوم بحذفه قبل لا تستدعي الدالة get.

كود (من مشاركة الزميل اسامه . )

RECT	*pr;

	while(1)
	{
		pr	= get();
		// ...
		// TODO: your work using [pr] RECT pointer
		//
		delete pr;
	}

بالنسبة للGlobal Variables .. أنا كنت اقصد ما قاله الاخ خالد .

اعتذر عن عدم توضيح الرد .. كنت احسب انه واضح :) .

واكيد كل ردود الشباب قرأتها واستفدت منها .. ولكن اخذت ما يتناسب مع ما اعمل عليه حاليا .فقط .

--------

الاخ بندر :

-المترجم هو الفيجوال ستوديو اخر موديل 2005 . :) .

-واستخدم الRelease .. ولكن نفس الحجم .

-البرنامج لا يستخدم اي مكتبة خارجية .. حتى IOstream .

ولكن مع ذلك الحجم كبير .. في الذاكرة .

بالنسبة للSmart Pointers هذي اول مرة اسمع عنها .. أنا توقعت اني صرت حرّيف في السي بلس وهاضمها من عقب ما تعلمت للinterface . . لكن الظاهر ان ما حولك أحد ..

تم تعديل هذه المشاركة بواسطة الشمري في 13 أبريل 2007 في 21:16

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#12

بالنسبة للمشكلة الاساسية ، فقد حلها الاعضاء

بالنسبة لحجم البرنامج ، اعتقد انك نسيت عدم اضافة مكتبات opengl ، ام انت لم تضفها من الاساس ؟؟ ممكن تكون نسيت ...

بالنسبة لsmart pointers هي من انواع النماذج Patterns هناك كتاب معروف لمجموعة الاربعة GOF اسمه Design Patterns ممتاز في هذه النوعية .

BEng , Electronics and communications.

Embedded systems engineer.

Graphics Programmer

عالم الكومبيوتر هو من لديه المعرفة في علوم الكومبيوتر ، الرياضيات ، هندسة الالكترونيات -احمد صالح

#13

أخوي الشمري

الSmart Pointer لا يعتبر من أساسيات لغة ++C . هو فقط عبارة عن Pattern مستخدمة في البرمجة مثل ما قال أحمد .

#14

اذا تستعمل كومبايلر MS VC++ اعرف انه غبي :)

جرب تستعمل ال g++ في اللينكس راح تلقاه 13 كيلو !!

#15
اقتباس
بالنسبة لحجم البرنامج ، اعتقد انك نسيت عدم اضافة مكتبات opengl ، ام انت لم تضفها من الاساس ؟؟ ممكن تكون نسيت

المشكلة هي في حجم البرنامج في الRAM . .

البرنامج هذا هو :

int main()
{

while(1)
{
}



	return 0;
}

وحجمه في الرام 560 كيلوبايت

اقتباس
جرب تستعمل ال g++ في اللينكس راح تلقاه 13 كيلو !!

صعبة شوي .. انا اعمل برنامجي الان في الVC2005 .. والبرنامج ايضا يعتمد على الويندوز.

تم تعديل هذه المشاركة بواسطة الشمري في 17 أبريل 2007 في 09:38

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

هذا الموضوع مغلق.

مواضيع مشابهة

عدد الزوار حالياً

المتواجدون خلال آخر دقيقتين · يتحدّث كل ٣٠ ثانية

—الإجمالي—أعضاء مسجّلون—زوار بدون تسجيل

جارٍ التحقق من المتواجدين…