مشكلة استخدام الذاكرة في ج
سؤال
الرجاء المساعدة :) نظام التشغيل :لينكس
حيث في" النوم(1000) ؛ "، في هذا الوقت" أعلى (عرض المهام لينكس) " كتب لي 7.7 ٪ استخدام م.فالغريند :لم يتم العثور على تسرب الذاكرة.
أنا أفهم ، كتب بشكل صحيح وجميع نتيجة مالوك لاغية.ولكن لماذا في هذا الوقت "النوم" برنامجي لا انخفضت الذاكرة ?ما مفقود ?
آسف على لغتي الإنجليزية السيئة ، شكرا
~ # tmp_soft
For : Is it free?? no
Is it free?? yes
For 0
For : Is it free?? no
Is it free?? yes
For 1
END : Is it free?? yes
END
~ #top
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
23060 root 20 0 155m 153m 448 S 0 7.7 0:01.07 tmp_soft
المصدر الكامل :تمب_ سوفت.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
struct cache_db_s
{
int table_update;
struct cache_db_s * p_next;
};
void free_cache_db (struct cache_db_s ** cache_db)
{
struct cache_db_s * cache_db_t;
while (*cache_db != NULL)
{
cache_db_t = *cache_db;
*cache_db = (*cache_db)->p_next;
free(cache_db_t);
cache_db_t = NULL;
}
printf("Is it free?? %s\n",*cache_db==NULL?"yes":"no");
}
void make_cache_db (struct cache_db_s ** cache_db)
{
struct cache_db_s * cache_db_t = NULL;
int n = 10000000;
for (int i=0; i = n; i++)
{
if ((cache_db_t=malloc(sizeof(struct cache_db_s)))==NULL) {
printf("Error : malloc 1 -> cache_db_s (no free memory) \n");
break;
}
memset(cache_db_t, 0, sizeof(struct cache_db_s));
cache_db_t->table_update = 1; // tmp
cache_db_t->p_next = *cache_db;
*cache_db = cache_db_t;
cache_db_t = NULL;
}
}
int main(int argc, char **argv)
{
struct cache_db_s * cache_db = NULL;
for (int ii=0; ii 2; ii++) {
make_cache_db(&cache_db);
printf("For : Is it free?? %s\n",cache_db==NULL?"yes":"no");
free_cache_db(&cache_db);
printf("For %d \n", ii);
}
printf("END : Is it free?? %s\n",cache_db==NULL?"yes":"no");
printf("END \n");
sleep(1000);
return 0;
}
المحلول
إذا كنت تحاول تحديد ما إذا كان البرنامج الخاص بك لديه تسرب الذاكرة ، ثم top
ليست الأداة المناسبة لهذا المنصب (valrind
هو).
top
يظهر استخدام الذاكرة كما يراها نظام التشغيل.حتى لو اتصلت free
, ، ليس هناك ما يضمن أن الذاكرة المحررة ستعود إلى نظام التشغيل.عادة ، فإنه لن.ومع ذلك ، فإن الذاكرة تصبح "حرة" بمعنى أن العملية الخاصة بك يمكن استخدامها لتخصيصات لاحقة.
تحرير إذا كان لديك libc
يدعم ذلك ، يمكنك محاولة تجريب M_TRIM_THRESHOLD
.حتى إذا اتبعت هذا المسار ، فسيكون الأمر صعبا (كتلة واحدة مستخدمة تجلس بالقرب من الجزء العلوي من الكومة ستمنع تحرير كل الذاكرة الخالية أسفلها إلى نظام التشغيل).
نصائح أخرى
لأسباب جيدة، لا توجد مخصص الذاكرة تقريبا إرجاع كتل إلى نظام التشغيل
لا يمكن إزالة الذاكرة إلا من برنامجك في وحدات الصفحات، وحتى أنه من غير المرجح أن يلاحظ ذلك.
caloc (3) و malloc (3) يتفاعل مع kernel للحصول على الذاكرة، إذا لزم الأمر. ولكن جدا، غير قليل جدا من تطبيقات مجانية (3) من أي وقت مضى إرجاع الذاكرة إلى kernel 1
حتى إذا أرادت حرة () إرجاع الذاكرة إلى النظام، فسيحتاج إلى صفحة ذاكرة واحدة على الأقل من أجل الحصول على النواة لتحمي المنطقة فعليا، لذلك فإن إطلاق كتلة صغيرة سيؤدي إلا إلى تغيير الحماية إذا كان الأمر آخر كتلة صغيرة في صفحة.
نظرية العملية
حتى يحصل maloc (3) على الذاكرة من kernel عندما يحتاجها، في نهاية المطاف في وحدات مضاعفات الصفحة المنفصلة. يتم تقسيم هذه الصفحات أو توحيدها كما يتطلب البرنامج. maloc والتعاون المجاني للحفاظ على دليل. إنهم يتجهون كتل مجانية مجاورة عندما يكون من الممكن أن تكون قادرا على توفير كتل كبيرة. قد يتضمن الدليل أو لا ينطوي على استخدام الذاكرة في كتل تحرير لتشكيل قائمة مرتبطة. (البديل هو أكثر الذاكرة مشتركة بعض الشيء والترحيل، ويشمل تخصيص الذاكرة على وجه التحديد لهذا الدليل.) ليس لديك القليل من القدرة على فرض الوصول إلى كتل الفردية حتى عند تجميع رمز تصحيح الأخطاء الخاص والاختياري البرنامج.
1. حقيقة أن بعض التطبيقات القليلة للغاية من المحاولة المجانية () لم يعد الذاكرة إلى النظام ليست على الإطلاق بسبب تجنب المنفذين.
تفاعل مع النواة أبطأ بكثير من ببساطة تنفيذ رمز المكتبة، وستكون الفائدة صغيرة. تحتوي معظم البرامج على بيانات مطردة أو زيادة ذاكرة الذاكرة، لذا فإن الوقت الذي يقضيه في تحليل كومة الذاكرة الكومة قد يضيع تماما. وتشمل الأسباب الأخرى حقيقة أن التجزئة الداخلي يجعل كتل محاذاة في الصفحات من غير المرجح أن تكون موجودة، ومن المحتمل أن تعيد كتلة ستعمل كتل جزء على أي من الجانبين. أخيرا، من المرجح أن تقوم البرامج القليلة التي تقوم بإرجاع كميات كبيرة من الذاكرة تجاوز malloc () وتخصيص صفحات مجانية على أي حال.
مجاني عموما () لا يعيد الذاكرة الفعلية إلى نظام التشغيل، لا يزال يتم تعيينه في الذاكرة الظاهرية في العملية الخاصة بك.إذا قمت بتخصيص جزء كبير من الذاكرة، فقد تخصيص LIBC بواسطة MMAP ()؛ثم إذا كنت تحررها، فقد تحدد LIBC الذاكرة إلى نظام التشغيل من قبل Munmap ()، في هذه الحالة، سيعرض Top أن استخدام ذاكرة الخاص بك ينخفض.
لذلك، إذا كنت تريد تحرير الذاكرة إلى نظام التشغيل بشكل صريح، فيمكنك استخدام mmap () / munmap ().
عندما كنت free()
الذاكرة ، يتم إرجاعها إلى مجموعة ذاكرة مكتبة سي القياسية ، ولا يتم إرجاعها إلى نظام التشغيل.في رؤية نظام التشغيل ، كما تراه من خلال top
, ، لا تزال العملية "تستخدم" هذه الذاكرة.ضمن هذه العملية ، شكلت مكتبة ج للذاكرة ويمكن أن يعود نفس المؤشر من malloc()
في المستقبل.
سأشرح ذلك أكثر مع بداية مختلفة:
أثناء المكالمات إلى malloc
, ، قد يحدد تطبيق المكتبة القياسي أن العملية لا تحتوي على ذاكرة مخصصة كافية من نظام التشغيل.في ذلك الوقت ، ستقوم المكتبة بإجراء مكالمة نظام لتلقي المزيد من الذاكرة من نظام التشغيل إلى العملية (على سبيل المثال, sbrk()
أو VirtualAlloc()
يدعو النظام على يونكس أو ويندوز ، على التوالي).
بعد أن تطلب المكتبة ذاكرة إضافية من نظام التشغيل ، فإنها تضيف هذه الذاكرة إلى بنية الذاكرة المتاحة للعودة منها malloc
.مكالمات لاحقة إلى malloc
سوف تستخدم هذه الذاكرة حتى تنفد.بعد ذلك ، تطلب المكتبة من نظام التشغيل المزيد من الذاكرة.
عندما كنت free
الذاكرة ، المكتبة عادة لا تعيد الذاكرة إلى نظام التشغيل.هناك العديد من الأسباب لذلك.أحد الأسباب هو أن مؤلف المكتبة يعتقد أنك ستتصل malloc
مجددا.إذا كنت لن تتصل malloc
مرة أخرى ، من المحتمل أن ينتهي برنامجك قريبا.في كلتا الحالتين ، لا توجد ميزة كبيرة لإعادة الذاكرة إلى نظام التشغيل.
سبب آخر لعدم قيام المكتبة بإعادة الذاكرة إلى نظام التشغيل هو أن الذاكرة من نظام التشغيل يتم تخصيصها في نطاقات كبيرة ومتجاورة.لا يمكن إرجاعه إلا عندما لا يكون النطاق المتجاور بأكمله قيد الاستخدام.نمط الدعوة malloc
و free
قد لا مسح مجموعة كاملة من الاستخدام.
مشكلتان:
في
make_cache_db()
, ، الخطfor (int i=0; i = n; i++)
ربما يجب أن تقرأ
for (int i=0; i<n; i++)
خلاف ذلك ، عليك تخصيص واحد فقط
cache_db_s
عقدة.الطريقة التي تعين بها
cache_db
فيmake_cache_db()
يبدو أن عربات التي تجرها الدواب.يبدو أن نيتك هي إرجاع مؤشر إلى العنصر الأول من القائمة المرتبطة;ولكن لأنك إعادة تعيينcache_db
في كل تكرار للحلقة ، سينتهي بك الأمر بإرجاع مؤشر إلى الاخير عنصر القائمة.إذا قمت لاحقا بتحرير القائمة باستخدام
free_cache_db()
, ، سيؤدي ذلك إلى تسرب الذاكرة.في الوقت الحالي ، على الرغم من ذلك ، يتم إخفاء هذه المشكلة عن طريق الخطأ الموضح في النقطة النقطية السابقة ، مما يؤدي إلى تخصيص قوائم بطول 1 فقط.
مستقلة عن هذه الأخطاء ، والنقطة التي أثارها إيكس صالحة جدا:لا تحتاج مكتبة وقت التشغيل إلى إرجاع الكل free()
د الذاكرة إلى نظام التشغيل.