ยังรั่วไหลได้ที่ตรวจพบโดย 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.