ยังรั่วไหลได้ที่ตรวจพบโดย Valgrind


155

ฟังก์ชั่นทั้งหมดที่กล่าวถึงในบล็อกนี้เป็นฟังก์ชั่นห้องสมุด ฉันจะแก้ไขการรั่วไหลของหน่วยความจำนี้ได้อย่างไร

มันอยู่ภายใต้หมวดหมู่" ยังเข้าถึงได้ " (มีอีก 4 ซึ่งคล้ายกันมาก แต่มีขนาดแตกต่างกัน)

 630 bytes in 1 blocks are still reachable in loss record 5 of 5
    at 0x4004F1B: calloc (vg_replace_malloc.c:418)
    by 0x931CD2: _dl_new_object (dl-object.c:52)
    by 0x92DD36: _dl_map_object_from_fd (dl-load.c:972)
    by 0x92EFB6: _dl_map_object (dl-load.c:2251)
    by 0x939F1B: dl_open_worker (dl-open.c:255)
    by 0x935965: _dl_catch_error (dl-error.c:178)
    by 0x9399C5: _dl_open (dl-open.c:584)
    by 0xA64E31: do_dlopen (dl-libc.c:86)
    by 0x935965: _dl_catch_error (dl-error.c:178)
    by 0xA64FF4: __libc_dlopen_mode (dl-libc.c:47)
    by 0xAE6086: pthread_cancel_init (unwind-forcedunwind.c:53)
    by 0xAE61FC: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)

Catch:เมื่อฉันรันโปรแกรมของฉันมันก็ไม่มีการรั่วไหลของหน่วยความจำ แต่มันมีหนึ่งบรรทัดเพิ่มเติมในเอาต์พุต Valgrind ซึ่งไม่เคยปรากฏมาก่อน:

ยกเลิก syms ที่ 0x5296fa0-0x52af438 ใน /lib/libgcc_s-4.4.4-20100100630.so.1 เนื่องจาก munmap ()

หากการรั่วไหลไม่สามารถแก้ไขได้ใครบางคนสามารถอธิบายสาเหตุที่บรรทัด munmap () ทำให้ Valgrind รายงานการรั่วไหลของ 0 "ยังเข้าถึงได้"

แก้ไข:

นี่คือตัวอย่างทดสอบเล็กน้อย:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void *runner(void *param) {
    /* some operations ... */
    pthread_exit(NULL);
}

int n;

int main(void) {

    int i;
    pthread_t *threadIdArray;

    n=10; /* for example */

    threadIdArray = malloc((n+n-1)*sizeof(pthread_t));  

    for(i=0;i<(n+n-1);i++) {
        if( pthread_create(&threadIdArray[i],NULL,runner,NULL) != 0 ) {
            printf("Couldn't create thread %d\n",i);
            exit(1);
        }
    }


    for(i=0;i<(n+n-1);i++) {
        pthread_join(threadIdArray[i],NULL);
    }

    free(threadIdArray);

    return(0);
}

ทำงานด้วย:

valgrind -v --leak-check=full --show-reachable=yes ./a.out

valgrind คำถามที่พบบ่อย
jww

คำตอบ:


379

มีมากกว่าหนึ่งวิธีในการกำหนด "หน่วยความจำรั่ว" โดยเฉพาะอย่างยิ่งมีสองคำจำกัดความหลักของ "memory รั่ว" ที่มีการใช้งานทั่วไปในหมู่โปรแกรมเมอร์

คำจำกัดความที่ใช้บ่อยครั้งแรกของ "หน่วยความจำรั่ว" คือ "หน่วยความจำได้รับการจัดสรรและไม่ได้ถูกปล่อยให้เป็นอิสระก่อนที่โปรแกรมจะถูกยกเลิก" อย่างไรก็ตามโปรแกรมเมอร์หลายคน (ถูกต้อง) ยืนยันว่าการรั่วไหลของหน่วยความจำบางประเภทที่ตรงกับคำนิยามนี้ไม่ได้ก่อให้เกิดปัญหาใด ๆ ดังนั้นจึงไม่ควรถูกพิจารณาว่าเป็น "หน่วยความจำรั่ว" จริง

คำจำกัดความที่เข้มงวดเนื้อหา (และมีประโยชน์มากขึ้น) ของ "การรั่วไหลของหน่วยความจำ" คือ "การจัดสรรหน่วยความจำและไม่สามารถปลดปล่อยได้ในภายหลังเนื่องจากโปรแกรมไม่มีตัวชี้ใด ๆ ไปยังบล็อกหน่วยความจำที่จัดสรร" คุณไม่สามารถเพิ่มหน่วยความจำที่คุณไม่มีพอยน์เตอร์ได้อีกต่อไป หน่วยความจำดังกล่าวจึงเป็น "หน่วยความจำรั่ว" Valgrind ใช้คำจำกัดความที่เข้มงวดมากขึ้นของคำว่า "memory รั่ว" นี่คือประเภทของการรั่วไหลซึ่งอาจทำให้เกิดการสูญเสียกองมากโดยเฉพาะอย่างยิ่งสำหรับกระบวนการที่มีอายุการใช้งานยาวนาน

หมวดหมู่ "ยังเข้าถึงได้" ภายในรายงานการรั่วไหลของ Valgrind หมายถึงการจัดสรรที่เหมาะสมกับคำจำกัดความแรกของ "หน่วยความจำรั่ว" เท่านั้น บล็อกเหล่านี้ไม่ได้ถูกปล่อยให้เป็นอิสระ แต่พวกมันอาจถูกทำให้เป็นอิสระ (ถ้าโปรแกรมเมอร์ต้องการ) เนื่องจากโปรแกรมยังคงติดตามตัวชี้ไปยังบล็อกหน่วยความจำเหล่านั้น

โดยทั่วไปไม่จำเป็นต้องกังวลเกี่ยวกับบล็อก "ยังเข้าถึงได้" พวกเขาไม่ก่อให้เกิดปัญหาที่หน่วยความจำรั่วจริงอาจทำให้เกิด ตัวอย่างเช่นปกติแล้วไม่มีความเป็นไปได้ที่ฮีปจะหมดจากบล็อก "ยังเข้าถึงได้" นี่เป็นเพราะบล็อกเหล่านี้มักจะจัดสรรครั้งเดียวอ้างอิงที่ถูกเก็บไว้ตลอดระยะเวลาของกระบวนการของชีวิต ในขณะที่คุณสามารถผ่านและตรวจสอบให้แน่ใจว่าโปรแกรมของคุณปลดปล่อยหน่วยความจำที่จัดสรรไว้ทั้งหมดโดยปกติแล้วจะไม่มีประโยชน์ในทางปฏิบัติเนื่องจากระบบปฏิบัติการจะเรียกคืนหน่วยความจำของกระบวนการทั้งหมดหลังจากกระบวนการยุติลง ตัดกันด้วยความจริง การรั่วไหลของหน่วยความจำซึ่งหากปล่อยทิ้งไว้ไม่รวมอาจทำให้กระบวนการหมดหน่วยความจำหากปล่อยทิ้งไว้นานพอหรือจะทำให้กระบวนการใช้หน่วยความจำมากเกินความจำเป็น

อาจเป็นเพียงครั้งเดียวที่มีประโยชน์เพื่อให้แน่ใจว่าการจัดสรรทั้งหมดมีการจับคู่ "อิสระ" คือถ้าเครื่องมือตรวจจับการรั่วไหลของคุณไม่สามารถบอกได้ว่าบล็อกใดที่ "เข้าถึงได้" (แต่ Valgrind สามารถทำได้) หรือระบบปฏิบัติการของคุณ หน่วยความจำของกระบวนการยุติ (แพลตฟอร์มทั้งหมดที่ Valgrind ได้รับการพอร์ตเพื่อทำสิ่งนี้)


คุณสามารถระบุได้ว่า munmap () กำลังทำอะไรที่ทำให้บล็อก "ยังเข้าถึงได้" หายไป?

3
@crypto: อาจเป็นไปได้ว่าmunmapมีการเรียกใช้เนื่องจากการยกเลิกการโหลดวัตถุที่ใช้ร่วมกัน และทรัพยากรทั้งหมดที่ใช้โดยวัตถุที่ใช้ร่วมกันอาจได้รับการปลดปล่อยก่อนที่จะถูกยกเลิกการโหลด สิ่งนี้สามารถอธิบายได้ว่าทำไม "ยังเข้าถึงได้" กำลังได้รับการปลดปล่อยในmunmapกรณีนี้ ฉันแค่คาดเดาที่นี่ แต่ ที่นี่มีข้อมูลไม่เพียงพอที่จะพูดได้อย่างแน่นอน
Dan Moulding

3
กรณีหนึ่งที่หน่วยความจำ "ยังเข้าถึงได้" อาจถือได้ว่าเป็นหน่วยความจำรั่ว: สมมติว่าคุณมีตารางแฮชที่คุณเพิ่มพอยน์เตอร์ไปยังฮีปที่จัดสรรหน่วยความจำเป็นค่า หากคุณยังคงแทรกรายการใหม่บนโต๊ะ แต่จะไม่ลบและปล่อยสิ่งที่คุณไม่ต้องการอีกต่อไปมันสามารถเติบโตได้อย่างไม่มีกำหนดและรั่วเหตุการณ์ฮีปหน่วยความจำหากหน่วยความจำนั้น "เข้าถึงได้" นี่เป็นกรณีของหน่วยความจำรั่วที่คุณมีในภาษาจาวาหรือภาษาที่รวบรวมขยะอื่น ๆ
lvella

ดูคำตอบนี้ในคำถามที่พบบ่อยเกี่ยวกับบล็อก "ยังเข้าถึงได้" ที่สร้างโดย STL valgrind.org/docs/manual/faq.html#faq.reports
John Perry

5
"โปรแกรมเมอร์หลายคน (ถูกต้อง) ยืนยันว่า [หน่วยความจำรั่วไหล] ไม่ก่อให้เกิดปัญหา [a] และดังนั้นจึงไม่ควรถูกพิจารณาว่าเป็นหน่วยความจำรั่วจริง" - ฮ่า ๆ ... สร้าง DLL ท้องถิ่นด้วยหน่วยความจำรั่วชนิดนั้น มี Java หรือ. Net กินมัน Java และ. Net โหลดและยกเลิกการโหลด DLL หลายพันครั้งในช่วงอายุการใช้งานของโปรแกรม แต่ละครั้งที่โหลด DLL อีกครั้งจะทำให้หน่วยความจำรั่วอีกเล็กน้อย โปรแกรมที่ใช้เวลานานในที่สุดหน่วยความจำจะหมด มันผลักดันผู้ดูแล OpenJDK ของ Debian อย่างบ้าคลั่ง เขากล่าวเช่นเดียวกันในรายชื่อผู้รับจดหมาย OpenSSL ในขณะที่เรากำลังพูดถึงการรั่วไหลของหน่วยความจำ "ใจดี" ของ OpenSSL
jww

10

เนื่องจากมีกิจวัตรบางอย่างจากตระกูล pthread ที่ด้านล่าง (แต่ฉันไม่รู้ว่าอันไหน), ฉันเดาว่าคงเป็นเพราะคุณได้เปิดตัวเธรดบางตัวที่เข้าร่วมได้ซึ่งยุติการทำงาน

pthread_joinข้อมูลรัฐทางออกของหัวข้อที่จะถูกเก็บไว้ใช้ได้จนกว่าคุณเรียก ดังนั้นหน่วยความจำจะถูกเก็บไว้ในบันทึกการสูญเสียเมื่อสิ้นสุดโปรแกรม แต่ยังสามารถเข้าถึงได้เนื่องจากคุณสามารถใช้pthread_joinเพื่อเข้าถึงได้

หากการวิเคราะห์นี้ถูกต้องให้เปิดตัวเธรดเหล่านี้ออกหรือเข้าร่วมก่อนที่จะยุติโปรแกรมของคุณ

แก้ไข : ฉันรันโปรแกรมตัวอย่างของคุณ (หลังจากแก้ไขบางอย่างชัดเจน) และฉันไม่มีข้อผิดพลาด แต่มีดังต่อไปนี้

==18933== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
--18933-- 
--18933-- used_suppression:      2 dl-hack3-cond-1
--18933-- used_suppression:      2 glibc-2.5.x-on-SUSE-10.2-(PPC)-2a

ตั้งแต่คล้ายสิ่งที่มากของสิ่งที่คุณเห็นฉันเดาว่าคุณเห็นปัญหาที่รู้กันว่ามีวิธีการแก้ปัญหาในแง่ของไฟล์สำหรับการปราบปรามdl- valgrindบางทีระบบของคุณอาจไม่ทันสมัยหรือการแจกจ่ายของคุณไม่ได้ดูแลสิ่งเหล่านี้ (ของฉันคือ Ubuntu 10.4, 64 บิต)


ฉันได้รับข้อผิดพลาด 0 เช่นเดียวกับคุณ โปรดตรวจสอบข้อมูลสรุปการรั่วไหลสำหรับข้อมูลเกี่ยวกับ "การรั่วไหล"

@crypto: ฉันไม่เข้าใจ คุณหมายถึงคุณมี supressions เช่นเดียวกับที่ฉันมี?
Jens Gustedt

used_suppression: 14 dl-hack3-cond-1 <- นั่นคือสิ่งที่ฉันได้รับ

6

คุณดูเหมือนจะไม่เข้าใจความstill reachableหมาย

อะไรstill reachableคือไม่รั่วไหล คุณไม่จำเป็นต้องทำอะไรเกี่ยวกับเรื่องนี้


24
สิ่งนี้ขัดแย้งกับ verbage อื่น ๆ ที่จัดทำโดย Valgrind รวมถึงความไม่ถูกต้องทางเทคนิค หน่วยความจำ "ยังสามารถเข้าถึงได้" ที่โปรแกรมออกและอาจทำให้เกิดการรั่วไหล ถ้าคุณแก้จุดบกพร่องรหัสให้ทำงานบน RTOS ซึ่งทำความสะอาดหน่วยความจำไม่ดีหลังจากออกจากโปรแกรม
Toymakerii

4
น่าเสียดายที่มันไม่จริงเสมอไป ยกตัวอย่างเช่นตัวอธิบายไฟล์ที่หายไปสามารถนับเป็นหน่วยความจำรั่ว แต่ valgrind จัดประเภทว่า "ยังเข้าถึงได้" น่าจะเป็นเพราะตัวชี้ที่นำพวกเขายังคงสามารถเข้าถึงได้ภายในตารางระบบ แต่สำหรับวัตถุประสงค์ของการดีบั๊กการวินิจฉัยที่แท้จริงคือ "การรั่วไหลของหน่วยความจำ"
สีฟ้า

ตัวอธิบายไฟล์ที่หายไปไม่ใช่การรั่วไหลของหน่วยความจำตามนิยาม บางทีคุณกำลังพูดถึงพFILEอยน์เตอร์ที่หายไป?
ว่าจ้างรัสเซีย

6

นี่คือคำอธิบายที่เหมาะสมของ "ยังเข้าถึงได้":

"ยังเข้าถึงได้" คือการรั่วไหลที่กำหนดให้กับตัวแปรโกลบอลและสแตติกท้องถิ่น เนื่องจาก valgrind ติดตามตัวแปรโกลบอลและสแตติกจึงสามารถแยกการจัดสรรหน่วยความจำที่ได้รับมอบหมาย "ครั้งหนึ่งแล้วลืม" ตัวแปรโกลบอลกำหนดการจัดสรรหนึ่งครั้งและไม่เคยกำหนดใหม่โดยปกติการจัดสรรจะไม่ใช่ "การรั่ว" ในแง่ที่ว่ามันไม่เติบโตอย่างไม่มีกำหนด มันยังคงรั่วไหลในความหมายที่เข้มงวด แต่มักจะถูกละเว้นเว้นแต่ว่าคุณเชื่องช้า

ตัวแปรโลคัลที่กำหนดให้กับการจัดสรรและไม่ว่างจะเกือบรั่วไหลเสมอ

นี่คือตัวอย่าง

int foo(void)
{
    static char *working_buf = NULL;
    char *temp_buf;
    if (!working_buf) {
         working_buf = (char *) malloc(16 * 1024);
    }
    temp_buf = (char *) malloc(5 * 1024);

    ....
    ....
    ....

}

Valgrind จะรายงาน working_buf ว่า "ยังเข้าถึงได้ - 16k" และ temp_buf เป็น "สูญหายอย่างแน่นอน - 5k"


-1

สำหรับผู้อ่านในอนาคต "ยังคงเข้าถึงได้" อาจหมายถึงคุณลืมปิดบางสิ่งบางอย่างเช่นไฟล์ แม้ว่ามันจะไม่เป็นเช่นนั้นในคำถามต้นฉบับ แต่คุณควรตรวจสอบให้แน่ใจว่าได้ทำเช่นนั้นแล้ว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.