Vũ Quang Huy Memory Leak – rò rỉ bộ nhớ xảy ra khi ta cấp phát một vùng nhớ và không thu hồi lại chúng sau khi sử dụng. Để tránh việc này xảy ra, theo lý thuyết là bất kể khi nào ta gọi toán tử new thì ngay sau đó ta cần gọi toán tử delete (tương ứng với cặp đôi malloc(...) - free(...)). Bài viết hướng dẫn bạn đọc sử dụng thư viện VLD để phát hiện memory leak.
Nội dung bài viết

Giới thiệu

Memory Leak – rò rỉ bộ nhớ xảy ra khi ta cấp phát một vùng nhớ và không thu hồi lại chúng sau khi sử dụng. Để tránh việc này xảy ra, theo lý thuyết là bất kể khi nào ta gọi toán tử new thì ngay sau đó ta cần gọi toán tử delete (tương ứng với cặp đôi malloc(...) - free(...)). Lý thuyết trên hoàn toàn đúng khi ta muốn quản lý và sử dụng bộ nhớ một cách có hiệu quả. Tuy nhiên, trong thực tế khi làm việc, tôi đã tiếp xúc với những dự án có đến hàng trăm, ngàn file khác nhau, và với mỗi file lại vài trăm cho tới vài ngàn dòng code. Đó là chưa kể những dự án này có những phần không phải do tôi viết ra mà kế thừa từ những đồng nghiệp, và trong đó ta có các lớp này chồng lớp khác, có các struct và các hàm cấp phát vùng nhớ …

Với lượng dữ liệu đồ sộ như vậy, việc xét xem ta đã hủy các vùng nhớ sau khi sử dụng mà không dùng bất kì một công cụ hỗ trợ nào, gần như là một việc tốn rất nhiều thời gian mà có khi lại không hiệu quả do sơ sót trong việc làm thủ công.

Chính vì vậy, với kinh nghiệm của mình, tôi muốn giới thiệu đến các bạn một thư viện hỗ trợ cho việc phát hiện Memory Leak – đó là Visual Leak Detector.

Trong bài viết này tôi sử dụng Visual Studio 2013 Community để minh họa, tùy phiên bản mà bạn đọc có thể có những khác biệt nhỏ.

Tiền đề bài viết

Trong bài viết có đề cập đến vấn đề memory leak mà mỗi lập trình viên rất hay thường gặp khi làm việc với memory. Mục tiêu của bài viết nhằm tạo ra để tham khảo cho học viên STDIO Training, và chia sẻ cho các nhà phát triển ứng dụng với C/C++ trên Visual Studio.

Visual Leak Detector là gì?

Visual Leak Detector (VLD) là một thư viện mã nguồn mở hoàn toàn miễn phí giúp cho ta có thể xác định được memory leak ở trong chương trình. VLD chỉ tương thích với Visual C++.

Để có thể sử dụng VLD, tùy theo phiên bản Visual Studio sử dụng mà ta tải version thích hợp về. Trong trường hợp của tôi vì tôi sử dụng Visual Studio 2013 Community do đó version tôi sử dụng là VLD v2.4rc2.

Cài đặt và sử dụng VLD

Cài đặt VLD

Như đã đề cập bên trên, sau khi việc tải xuống hoàn tất ta tiến hành cài đặt VLD vào máy tính. Để tiện lợi trong quá trình sử dụng các bạn có thể giữ nguyên phần cấu hình cài đặt mặc định.

Nếu quá trình cài đặt thành công, trong folder Program Files sẽ có thêm folder Visual Leak Detector với cấu trúc như sau:

ss_1

Sử dụng VLD

Muốn sử dụng VLD, ta đơn giản chỉ cần thêm include sau vào tất cả các file .cpp

#include <vld.h>

Để minh họa cho việc sử dụng VLD, đầu tiên ta cần giả lập môi trường ứng dụng đang có memory leak bằng việc cấp phát một vùng nhớ ngẫu nhiên và “cố tình” không gọi lệnh hủy chúng.

#include <string.h>

void main()
{
    char* p = new char[1024];
    char* str = strdup("Hello Stdio !");
}

Nếu ta không khai báo sử dụng VLD, đoạn chương trình sau sẽ kết thúc mà không có bất cứ thông báo gì đặc biệt.

Tiếp theo ta thử nghiệm bằng việc khai báo sử dụng thư viện VLD như trong đoạn mã dưới đây:

#include <vld.h>
#include <string.h>

void main()
{
    char* p = new char[1024];
    char* str = strdup("Hello Stdio !");
}

Khi đó, chương trình của chúng ta kết thúc, VLD sẽ giúp ta lần lượt rà soát lại tất cả các vùng nhớ ta đã cấp phát mà chưa gọi lệnh hủy. Và điểm đặc biệt nhất là VLD thông báo cho ta một cách rất chi tiết về vùng nhớ bị leak: kích thước, dữ liệu, call stack … như trong ví dụ dưới đây:

Visual Leak Detector Version 2.4RC2 installed.
WARNING: Visual Leak Detector detected memory leaks!
---------- Block 1 at 0x0044DC58: 1024 bytes ----------
  Leak Hash: 0xABCDB444, Count: 1, Total 1024 bytes
  Call Stack (TID 18000):
    0x0F87C260 (File and line number not available): MSVCR120D.dll!operator new
    c:\users\huy.vuquang\documents\visual studio 2013\projects\stdio_vld_demo\main.cpp (6): STDIO_VLD_Demo.exe!main + 0xA bytes
    f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c (626): STDIO_VLD_Demo.exe!__tmainCRTStartup + 0x19 bytes
    f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c (466): STDIO_VLD_Demo.exe!mainCRTStartup
    0x7648919F (File and line number not available): KERNEL32.DLL!BaseThreadInitThunk + 0xE bytes
    0x77BD0BBB (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x84 bytes
    0x77BD0B91 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x5A bytes
  Data:
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........


---------- Block 2 at 0x0044E088: 14 bytes ----------
  Leak Hash: 0x9E7F558B, Count: 1, Total 14 bytes
  Call Stack (TID 18000):
    0x0F7D80F0 (File and line number not available): MSVCR120D.dll!strdup
    c:\users\huy.vuquang\documents\visual studio 2013\projects\stdio_vld_demo\main.cpp (7): STDIO_VLD_Demo.exe!main + 0xD bytes
    f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c (626): STDIO_VLD_Demo.exe!__tmainCRTStartup + 0x19 bytes
    f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c (466): STDIO_VLD_Demo.exe!mainCRTStartup
    0x7648919F (File and line number not available): KERNEL32.DLL!BaseThreadInitThunk + 0xE bytes
    0x77BD0BBB (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x84 bytes
    0x77BD0B91 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x5A bytes
  Data:
    48 65 6C 6C    6F 20 53 74    64 69 6F 20    21 00           Hello.St dio.!...


Visual Leak Detector detected 2 memory leaks (1110 bytes).
Largest number used: 1110 bytes.
Total allocations: 1110 bytes.
Visual Leak Detector is now exiting.

Lúc này, công việc của ta đơn giản là đến dòng code, xem xét vùng nhớ được cấp phát như thế nào và tìm thời điểm thích hợp để hủy nó.

#include <vld.h>
#include <string.h>

void main()
{
    char* p = new char[1024];
    char* str = _strdup("Hello Stdio !");

    delete[] p;
    delete[] str;
}

Nếu VLD thông báo như dưới đây, thì ta có thể chắc chắn là ứng dụng không còn memory leak nữa.

Visual Leak Detector Version 2.4RC2 installed.
No memory leaks detected.
Visual Leak Detector is now exiting.
THẢO LUẬN
ĐÓNG