การตรวจจับหน่วยความจำรั่ว
พื้นฐาน memcheck
การตรวจจับหน่วยความจำรั่ว เป็นบทเรียน C Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน
หน่วยความจำรั่วคืออะไร
หน่วยความจำรั่ว เกิดขึ้นเมื่อคุณจัดสรรหน่วยความจำบน HEAP ด้วย malloc, calloc หรือ realloc แล้วสูญเสียพอยน์เตอร์ทั้งหมดที่ชี้ไปยังหน่วยความจำนั้นก่อนเรียก free
บล็อกดังกล่าวยังคงถูกจองไว้แต่ไม่สามารถเข้าถึงได้จนกว่ากระบวนการจะสิ้นสุด ในโปรแกรมที่ทำงานเป็นเวลานาน หน่วยความจำรั่วจะสะสมจนหน่วยความจำหมด
หน่วยความจำรั่วแบบพื้นฐาน
โปรแกรมนี้ทำให้หน่วยความจำรั่วหนึ่งบล็อก ขนาด 40 ไบต์
พอยน์เตอร์ p เป็นตัวแปรภายใน เมื่อ main คืนค่า p จะหายไป แต่บล็อกบน HEAP ที่เคยชี้อยู่จะไม่ถูกคืนหน่วยความจำ
#include <stdlib.h>
int main(void) {
int *p = malloc(10 * sizeof(int));
p[0] = 1;
return 0; /* never free(p) */
}การเรียกใช้ memcheck
memcheck เป็นเครื่องมือเริ่มต้น ดังนั้นคุณจึงไม่จำเป็นต้องระบุชื่อเครื่องมือ
เพิ่ม --leak-check=full เพื่อรับเส้นทางการเรียกฟังก์ชันสำหรับการจัดสรรแต่ละรายการที่รั่ว
gcc -g -O0 -o leak leak.c
valgrind --leak-check=full ./leakสรุป LEAK
เมื่อสิ้นสุดการทำงาน Valgrind จะแสดง LEAK SUMMARY สำหรับโปรแกรมข้างต้น ผลลัพธ์จะมีลักษณะประมาณนี้:
definitely lost: 40 bytes in 1 blocks
เส้นทางการเรียกจะชี้ไปยังตำแหน่งที่เรียก malloc ได้อย่างแม่นยำ ทำให้คุณทราบว่าการจัดสรรใดไม่ได้คืนหน่วยความจำ
หมวดหมู่ของหน่วยความจำรั่วทั้งสี่
memcheck จัดประเภทหน่วยความจำที่สูญหายออกเป็นสี่กลุ่ม:
- สูญหายแน่นอน — ไม่เหลือพอยน์เตอร์อยู่ เป็นหน่วยความจำรั่วจริง
- สูญหายทางอ้อม — เข้าถึงได้ผ่านบล็อกที่สูญหายแน่นอนเท่านั้น
- อาจสูญหาย — เหลือเพียงพอยน์เตอร์ที่ชี้ไปยังส่วนภายใน
- ยังเข้าถึงได้ — มีพอยน์เตอร์อยู่เมื่อสิ้นสุดการทำงาน แต่คุณไม่เคยคืนหน่วยความจำ
สูญหายแน่นอนเทียบกับยังเข้าถึงได้
สูญหายแน่นอน เป็นหมวดหมู่เร่งด่วน เพราะไม่สามารถกู้คืนไบต์เหล่านั้นได้อีกขณะที่โปรแกรมยังทำงาน
ยังเข้าถึงได้ มีความรุนแรงน้อยกว่า บล็อกนั้นรั่วเมื่อสิ้นสุดการทำงาน แต่ยังมีพอยน์เตอร์ระดับโกลบอลหรือแบบคงอยู่ชี้อ้างอิงอยู่ จึงมักเป็นการจัดสรรเพียงครั้งเดียวที่คุณไม่ได้คืนหน่วยความจำ
หน่วยความจำรั่วทางอ้อม
เมื่อคุณสูญเสียส่วนหัวของโครงสร้างแบบเชื่อมโยง โหนดทุกตัวจะไม่สามารถเข้าถึงได้
ส่วนหัวจะแสดงเป็น สูญหายแน่นอน และส่วนที่เหลือจะแสดงเป็น สูญหายทางอ้อม การแก้สาเหตุหลัก ซึ่งก็คือส่วนหัวที่สูญหาย จะกู้คืนหน่วยความจำทั้งหมดได้
struct Node { int v; struct Node *next; };
struct Node *make(void) {
struct Node *h = malloc(sizeof *h);
h->next = malloc(sizeof *h);
h->next->next = NULL;
return h;
}
/* if the caller drops the returned head, both nodes leak */เวอร์ชันที่แก้ไขแล้ว
คืนหน่วยความจำทุกส่วนที่จัดสรร เวอร์ชันนี้ไม่มีหน่วยความจำรั่ว
เมื่อทำงานภายใต้ Valgrind จะแสดงรายงานว่า All heap blocks were freed -- no leaks are possible
#include <stdlib.h>
int main(void) {
int *p = malloc(10 * sizeof(int));
p[0] = 1;
free(p);
return 0;
}การนับการจัดสรร
แม้จะไม่มีหน่วยความจำรั่ว HEAP SUMMARY ก็ยังบอกได้ว่าโปรแกรมของคุณทำงานกับหน่วยความจำมากเพียงใด:
total heap usage: 1 allocs, 1 frees, 40 bytes allocated
เมื่อจำนวนการจัดสรรและการคืนหน่วยความจำเท่ากัน แสดงว่าการทำงานสะอาด ไม่มีความผิดปกติ หากจำนวนไม่ตรงกัน นั่นคือเบาะแสแรกว่ามีบางสิ่งหลุดรอดไป
การแสดงบล็อกที่ยังเข้าถึงได้
โดยค่าเริ่มต้น memcheck จะไม่แสดงรายละเอียดของบล็อกที่ ยังเข้าถึงได้ หากต้องการตรวจสอบบล็อกเหล่านั้นด้วย ให้เพิ่ม:
--show-leak-kinds=all
คำสั่งนี้จะบังคับให้แสดงเส้นทางการเรียกสำหรับทุกหมวดหมู่ เหมาะเมื่อคุณต้องการให้โปรแกรมคืนหน่วยความจำทุกส่วนอย่างสมบูรณ์ก่อนสิ้นสุดการทำงาน
valgrind --leak-check=full --show-leak-kinds=all ./leakหน่วยความจำรั่วบนเส้นทางข้อผิดพลาด
หน่วยความจำรั่วที่ตรวจจับได้ยากที่สุดมักซ่อนอยู่บนเส้นทางที่คืนค่าก่อนกำหนด ในกรณีนี้ หากการจัดสรรครั้งที่สองล้มเหลว บล็อกแรกจะรั่ว
การทดสอบทั่วไปแทบไม่เข้าสู่แขนงเหล่านี้ แต่ memcheck จะตรวจจับได้ทุกครั้งที่มีการทำงานบนเส้นทางดังกล่าว ก่อนคืนค่าจากข้อผิดพลาด ควรคืนหน่วยความจำทุกส่วนที่ถืออยู่เสมอ
char *a = malloc(100);
char *b = malloc(100);
if (!b) {
/* bug: a is leaked here */
return -1;
}
/* fix: free(a); before returning */ตรวจสอบความเข้าใจ
เลือกหมวดหมู่ของหน่วยความจำรั่วที่ต้องได้รับการแก้ไขอย่างเร่งด่วนที่สุด
สรุปทบทวน
ตอนนี้คุณสามารถตรวจจับหน่วยความจำรั่วด้วย memcheck ได้แล้ว:
- เรียกใช้
valgrind --leak-check=full ./progกับไบนารีที่คอมไพล์ด้วย-g - อ่าน LEAK SUMMARY และ HEAP SUMMARY
- ให้ความสำคัญกับ สูญหายแน่นอน ก่อน แล้วติดตามหน่วยความจำรั่วทางอ้อมกลับไปยังสาเหตุหลัก
- ใช้
--show-leak-kinds=allเพื่อตรวจสอบบล็อกที่ยังเข้าถึงได้
ถัดไป: การเข้าถึงหน่วยความจำที่ไม่ถูกต้อง
คำถามที่พบบ่อย
บทเรียน “การตรวจจับหน่วยความจำรั่ว” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การตรวจจับหน่วยความจำรั่ว” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การตรวจจับหน่วยความจำรั่ว”
พื้นฐาน memcheck คุณปฏิบัติ C Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “การตรวจจับหน่วยความจำรั่ว” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C Academy นี้ได้ไหม
ได้ บทเรียน C Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- เหตุใดจึงใช้ Valgrind
- การตรวจจับหน่วยความจำรั่ว
- การเข้าถึงที่ไม่ถูกต้อง
- การอ่านรายงาน