Làm thế nào để tìm rò rỉ bộ nhớ trong mã / dự án C ++?


180

Tôi là một lập trình viên C ++ trên nền tảng Windows. Tôi đang sử dụng Visual Studio 2008.

Tôi thường kết thúc trong mã với rò rỉ bộ nhớ.

Thông thường tôi thấy rò rỉ bộ nhớ bằng cách kiểm tra mã, nhưng nó cồng kềnh và không phải lúc nào cũng là một cách tiếp cận tốt.

Vì tôi không thể mua một công cụ phát hiện rò rỉ bộ nhớ phải trả tiền, tôi muốn các bạn đề xuất những cách tốt nhất có thể để tránh rò rỉ bộ nhớ.

  1. Tôi muốn biết làm thế nào các lập trình viên có thể tìm thấy rò rỉ bộ nhớ.
  2. Có bất kỳ tiêu chuẩn hoặc thủ tục nào nên tuân theo để đảm bảo không có rò rỉ bộ nhớ trong chương trình không?

29
"Tôi thường kết thúc trong mã bị rò rỉ bộ nhớ." Nếu bạn sử dụng biến tự động, bộ chứa và con trỏ thông minh (và tuân theo các thực tiễn tốt nhất để sử dụng con trỏ thông minh), rò rỉ bộ nhớ sẽ cực kỳ hiếm. Hãy nhớ rằng, trong hầu hết các trường hợp, bạn nên sử dụng quản lý tài nguyên tự động .
James McNellis

Các vấn đề trùng lặp được bao phủ bởi một số câu hỏi, như stackoverflow.com/questions/1502799/ và và stackoverflow.com/questions/2820223/
Thẻ

1
@Hostile Fork: "làm thế nào người ta có thể tránh để thường kết thúc mã bị rò rỉ bộ nhớ" không nằm trong những câu trả lời đó.
Doc Brown

2
@Doc Brown: Không cảm thấy muốn tìm kiếm điều đó, nhưng tất cả đều được đề cập ở nơi khác, chẳng hạn như stackoverflow.com/questions/45627/ chủ
HostileFork nói rằng đừng tin vào SE

1
Trình phát hiện rò rỉ DIY: Bạn có thể đặt mã nghi ngờ vào một vòng lặp vô hạn và sau đó mở trình quản lý tác vụ, thông thường, một rò rỉ nhỏ sẽ lấp đầy bộ nhớ trong vài giây hoặc vài phút (Điều đó phụ thuộc vào độ phức tạp của mã và CPU của bạn). Nếu điều đó không xảy ra, đoạn mã đó có thể không bị rò rỉ.
Xin chào thế giới

Câu trả lời:


270

Hướng dẫn

Những thứ bạn cần

  • Thành thạo C ++
  • Trình biên dịch C ++
  • Trình gỡ lỗi và các công cụ phần mềm điều tra khác

1

Hiểu những điều cơ bản của người vận hành. Toán tử C ++ newphân bổ bộ nhớ heap. Các deletegiải phóng hành bộ nhớ heap. Đối với mọi người new, bạn nên sử dụng deleteđể bạn giải phóng cùng bộ nhớ bạn đã phân bổ:

char* str = new char [30]; // Allocate 30 bytes to house a string.

delete [] str; // Clear those 30 bytes and make str point nowhere.

2

Chỉ phân bổ lại bộ nhớ nếu bạn đã xóa. Trong mã dưới đây, strcó được một địa chỉ mới với phân bổ thứ hai. Địa chỉ đầu tiên bị mất không thể cứu vãn được, và 30 byte mà nó trỏ đến. Bây giờ chúng không thể giải phóng và bạn bị rò rỉ bộ nhớ:

char* str = new char [30]; // Give str a memory address.

// delete [] str; // Remove the first comment marking in this line to correct.

str = new char [60]; /* Give str another memory address with
                                                    the first one gone forever.*/

delete [] str; // This deletes the 60 bytes, not just the first 30.

3

Xem các bài tập con trỏ. Mỗi biến động (bộ nhớ được phân bổ trên heap) cần được liên kết với một con trỏ. Khi một biến động trở nên tách rời khỏi (các) con trỏ của nó, nó sẽ không thể xóa được. Một lần nữa, điều này dẫn đến rò rỉ bộ nhớ:

char* str1 = new char [30];

char* str2 = new char [40];

strcpy(str1, "Memory leak");

str2 = str1; // Bad! Now the 40 bytes are impossible to free.

delete [] str2; // This deletes the 30 bytes.

delete [] str1; // Possible access violation. What a disaster!

4

Hãy cẩn thận với con trỏ địa phương. Một con trỏ bạn khai báo trong một hàm được phân bổ trên ngăn xếp, nhưng biến động mà nó trỏ tới được phân bổ trên heap. Nếu bạn không xóa nó, nó sẽ tồn tại sau khi chương trình thoát khỏi chức năng:

void Leak(int x){

char* p = new char [x];

// delete [] p; // Remove the first comment marking to correct.

}

5

Hãy chú ý đến dấu ngoặc vuông sau khi "xóa". Sử dụng deletechính nó để giải phóng một đối tượng duy nhất. Sử dụng delete []với dấu ngoặc vuông để giải phóng một mảng heap. Đừng làm điều gì đó như thế này:

char* one = new char;

delete [] one; // Wrong

char* many = new char [30];

delete many; // Wrong!

6

Nếu rò rỉ chưa được cho phép - tôi thường tìm kiếm nó bằng deleaker (kiểm tra tại đây: http://deleaker.com ).


3
xin lỗi cho câu hỏi-bình luận nhưng những gì về tham số chức năng mà không có con trỏ? someFunction("some parameter")Tôi có phải xóa "some parameter"trong someFunction, sau khi gọi hàm, hoặc chúng sẽ tự động bị xóa?
19greg96

1
cảm ơn bạn đã liên kết đến Deleaker, đây là một công cụ thực sự tiện dụng với sự tích hợp gọn gàng vào phòng thu trực quan. Tôi có thể tiết kiệm rất nhiều thời gian sử dụng nó. chỉ cho tôi các dòng nơi tôi phân bổ bộ nhớ và không giải phóng nó. Tuyệt quá. Và nó là giá rẻ, so với các công cụ tìm rò rỉ bộ nhớ khác mà tôi tìm thấy.
đây là

@ john smith plz giải thích cách thích hợp để xử lý các trường hợp tương tự như trường hợp 3; str2 = str1; // Xấu! Bây giờ 40 byte là không thể miễn phí. Làm thế nào để xóa str 1 rồi ??
Nihar

1
Điều gì sẽ xảy ra nếu chúng ta sử dụng loại giá trị như char *, int, float, ... và struct như Vector, CString và không sử dụng bất kỳ toán tử 'mới' nào, nó sẽ không gây rò rỉ bộ nhớ, phải không?
123iamking

Tôi chỉ ở đây để nói rằng tôi đã không chạm vào c ++ trong gần 14 năm ... nhưng tôi tự hào nói rằng tôi đã hiểu và nhớ cách làm tất cả những điều này nhờ vào một cuốn sách c ++ mà tôi vẫn sở hữu và đọc khi tôi m chán với c #. Cuốn sách đó là C ++ hiệu quả của Scott Mitchell. Chúa tôi yêu cuốn sách đó. Cảm ơn Scott!
JonH

33

Bạn có thể sử dụng một số kỹ thuật trong mã của mình để phát hiện rò rỉ bộ nhớ. Cách phổ biến nhất và dễ dàng nhất để phát hiện là, xác định macro nói, DEBUG_NEW và sử dụng nó, cùng với các macro được xác định trước như __FILE____LINE__để xác định vị trí rò rỉ bộ nhớ trong mã của bạn. Các macro được xác định trước này cho bạn biết tệp và số dòng rò rỉ bộ nhớ.

DEBUG_NEW chỉ là một MACRO thường được định nghĩa là:

#define DEBUG_NEW new(__FILE__, __LINE__)
#define new DEBUG_NEW

Vì vậy, bất cứ nơi nào bạn sử dụng new, nó cũng có thể theo dõi tệp và số dòng có thể được sử dụng để xác định vị trí rò rỉ bộ nhớ trong chương trình của bạn.

__FILE__, __LINE__các macro được xác định trước sẽ đánh giá tên tệp và số dòng tương ứng nơi bạn sử dụng chúng!

Đọc bài viết sau đây giải thích kỹ thuật sử dụng DEBUG_NEW với các macro thú vị khác, rất đẹp:

Trình phát hiện rò rỉ bộ nhớ đa nền tảng


Từ Wikpedia ,

Debug_new đề cập đến một kỹ thuật trong C ++ để quá tải và / hoặc xác định lại toán tử mới và xóa toán tử để chặn các lệnh gọi cấp phát và phân bổ bộ nhớ, do đó gỡ lỗi một chương trình để sử dụng bộ nhớ. Nó thường liên quan đến việc xác định một macro có tên DEBUG_NEW và làm cho cái mới trở thành một cái gì đó giống như mới (_ FILE _, _ LINE _) để ghi lại thông tin tệp / dòng trên cấp phát.Microsoft Visual C ++ sử dụng kỹ thuật này trong Microsoft Foundation Classes. Có một số cách để mở rộng phương pháp này để tránh sử dụng xác định lại macro trong khi vẫn có thể hiển thị thông tin tệp / dòng trên một số nền tảng. Có nhiều hạn chế cố hữu đối với phương pháp này. Nó chỉ áp dụng cho C ++ và không thể bắt rò rỉ bộ nhớ bởi các chức năng C như malloc. Tuy nhiên, nó có thể rất đơn giản để sử dụng và cũng rất nhanh, khi so sánh với một số giải pháp gỡ lỗi bộ nhớ hoàn chỉnh hơn.


4
điều này #definesẽ gây rối với quá tải operator newvà tạo ra lỗi biên dịch. Ngay cả khi bạn thành công để khắc phục điều đó thì các chức năng quá tải vẫn sẽ không được xử lý. Mặc dù kỹ thuật này là tốt, đôi khi nó cần nhiều thay đổi mã.
iammilind

1
@iammilind: Tất nhiên, kỹ thuật này không phải là giải pháp khắc phục tất cả các vấn đề và chắc chắn không thể áp dụng trong mọi tình huống.
Nawaz

@Chris_vr: auto_ptrsẽ không hoạt động với các thùng chứa tiêu chuẩn như std::vector, std::listv.v. Xem điều này: stackoverflow.com/questions/111478/ chủ
Nawaz

Được, tuyệt đấy. TẬP_TIN và dòng được mô tả. Các operator newphiên bản của nó là gì và bạn đang sử dụng là gì?

14

Có một số kỹ thuật lập trình nổi tiếng sẽ giúp bạn giảm thiểu rủi ro bị rò rỉ bộ nhớ ngay từ đầu:

  • nếu bạn phải thực hiện cấp phát bộ nhớ động của riêng mình, hãy viết newdeleteluôn luôn theo cặp và đảm bảo mã phân bổ / giải quyết được gọi là cặp đôi
  • tránh phân bổ bộ nhớ động nếu bạn có thể. Ví dụ: sử dụng vector<T> tbất cứ nơi nào có thể thay vìT* t = new T[size]
  • sử dụng "con trỏ thông minh" như tăng con trỏ thông minh ( http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/smart_ptr.htm )
  • Sở thích cá nhân của tôi: đảm bảo bạn đã hiểu khái niệm quyền sở hữu của một con trỏ và đảm bảo rằng mọi nơi bạn sử dụng con trỏ, bạn đều biết thực thể mã nào là chủ sở hữu
  • tìm hiểu các hàm tạo / toán tử gán nào được trình biên dịch C ++ tạo tự động và điều đó có nghĩa là gì nếu bạn có lớp sở hữu một con trỏ (hoặc điều đó có nghĩa là nếu bạn có một lớp chứa con trỏ tới một đối tượng mà nó không sở hữu).

Tôi sử dụng auto_pulum của một đối tượng có nghĩa là nó sẽ xóa tất cả các con trỏ đối tượng lớp khác bên trong nó.
Chris_vr

@Chris_vr: nếu bạn có một câu hỏi cụ thể về auto_pulum, tôi sẽ đề nghị bạn tạo một câu hỏi mới, bao gồm một ví dụ.
Doc Brown

Nhiều bài viết cho tôi biết rằng vectơ <> không đảm bảo bộ nhớ được giải phóng khi rõ ràng. Cá nhân tôi đã thử nghiệm công cụ trao đổi, v.v. và tôi đã đi đến kết luận rằng vectơ <> bị rò rỉ đặc biệt là khi được sử dụng một cách linh hoạt. Tôi không hiểu làm thế nào vectơ <> có thể được tư vấn về phân bổ động tự làm bằng cách sử dụng 'mới' và dọn dẹp chính xác. Trong các chương trình nhúng của tôi, tôi tránh sử dụng vectơ <> cho các công cụ động vì tất cả các rò rỉ. Ở đó tôi sử dụng new hoặc std :: list
bart s

Tôi gõ một lệnh thứ hai vì số lượng ký tự. Đáng tiếc là trong c của tôi nhúng ++ Tôi có một c cũ ++ (? 98) mà không có shrink_to_fit trên một vector ... Tuy nhiên các chương trình nhúng là chắc chắn 100% hoàn toàn sụp đổ khi chạy ra khỏi bộ nhớ sử dụng vector <> động
bart s


8
  1. Tải xuống công cụ gỡ lỗi cho Windows .
  2. Sử dụng gflagstiện ích để bật theo dõi ngăn xếp chế độ người dùng.
  3. Sử dụng UMDHđể chụp nhiều ảnh chụp bộ nhớ chương trình của bạn. Chụp ảnh nhanh trước khi bộ nhớ được phân bổ và chụp ảnh nhanh thứ hai sau một điểm mà bạn tin rằng chương trình của bạn đã bị rò rỉ bộ nhớ. Bạn có thể muốn thêm tạm dừng hoặc lời nhắc trong chương trình của mình để cho bạn cơ hội chạy UMDHvà chụp ảnh nhanh.
  4. Chạy UMDHlại, lần này trong chế độ của nó có sự khác biệt giữa hai ảnh chụp nhanh. Sau đó, nó sẽ tạo ra một báo cáo có chứa các ngăn xếp cuộc gọi bị rò rỉ bộ nhớ bị nghi ngờ.
  5. Khôi phục gflagscài đặt trước đó của bạn khi bạn hoàn tất.

UMDHsẽ cung cấp cho bạn nhiều thông tin hơn heap gỡ lỗi CRT vì nó đang xem phân bổ bộ nhớ trong toàn bộ quá trình của bạn; nó thậm chí có thể cho bạn biết nếu các thành phần của bên thứ ba bị rò rỉ.


1
Tôi thích Deleaker và Valgrind thay vì trình hồ sơ tiêu chuẩn
z0r1fan

8

Chạy "Valgrind" có thể:

1) Trợ giúp Xác định Rò rỉ Bộ nhớ - cho bạn biết bạn có bao nhiêu rò rỉ bộ nhớ và chỉ ra các dòng trong mã nơi bộ nhớ bị rò rỉ được phân bổ.

2) Chỉ ra những nỗ lực sai đối với bộ nhớ trống (ví dụ: cuộc gọi không chính xác delete)

Hướng dẫn sử dụng "Valgrind"

1) Nhận valgrind ở đây .

2) Biên dịch mã của bạn bằng -gcờ

3) Trong shell của bạn chạy:

valgrind --leak-check=yes myprog arg1 arg2

Trong đó "myprog" là chương trình được biên dịch của bạn và arg1, arg2các đối số của chương trình của bạn.

4) Kết quả là một danh sách các cuộc gọi đến malloc/ newkhông có các cuộc gọi tiếp theo để xóa miễn phí.

Ví dụ:

==4230==    at 0x1B977DD0: malloc (vg_replace_malloc.c:136)

==4230==    by 0x804990F: main (example.c:6)

Cho bạn biết dòng nào malloc(không được giải phóng) được gọi.

Như được chỉ ra bởi những người khác, hãy đảm bảo rằng với mỗi new/ malloccuộc gọi, bạn có cuộc gọi delete/ freecuộc gọi tiếp theo .


6

Nếu bạn sử dụng gcc, có sẵn gprof.

Tôi muốn biết làm thế nào lập trình viên tìm thấy rò rỉ bộ nhớ

Một số sử dụng các công cụ, một số làm những gì bạn làm, cũng có thể thông qua đánh giá mã ngang hàng

Có bất kỳ tiêu chuẩn hoặc thủ tục nào nên tuân theo để đảm bảo không có rò rỉ bộ nhớ trong chương trình

Đối với tôi: bất cứ khi nào tôi tạo các đối tượng được phân bổ động, tôi luôn đặt mã giải phóng sau đó, sau đó điền mã vào giữa. Điều này sẽ ổn nếu bạn chắc chắn sẽ không có ngoại lệ trong mã giữa. Mặt khác, tôi sử dụng thử-cuối cùng (tôi không sử dụng C ++ thường xuyên).


đôi khi chúng ta không thể xóa được phân bổ trong constructor. phải làm gì trong dịp đó.
Chris_vr

5
  1. Trong phòng thu trực quan, có một trình phát hiện tích hợp để rò rỉ bộ nhớ gọi là Thư viện C Runtime. Khi chương trình của bạn thoát sau khi chức năng chính trở lại, CRT sẽ kiểm tra đống gỡ lỗi của ứng dụng của bạn. nếu bạn có bất kỳ khối nào vẫn được phân bổ trên heap debug, thì bạn bị rò rỉ bộ nhớ ..

  2. Diễn đàn này thảo luận về một số cách để tránh rò rỉ bộ nhớ trong C / C ++ ..


5

Tìm kiếm mã của bạn cho các lần xuất hiện newvà đảm bảo rằng tất cả chúng xảy ra trong một hàm tạo có xóa phù hợp trong hàm hủy. Hãy chắc chắn rằng đây là hoạt động ném duy nhất có thể có trong hàm tạo đó. Một cách đơn giản để làm điều này là bọc tất cả các con trỏ vào std::auto_ptr, hoặc boost::scoped_ptr(tùy thuộc vào việc bạn có cần di chuyển ngữ nghĩa hay không). Đối với tất cả các mã trong tương lai, chỉ cần đảm bảo rằng mọi tài nguyên được sở hữu bởi một đối tượng dọn sạch tài nguyên trong hàm hủy của nó. Nếu bạn cần di chuyển ngữ nghĩa thì bạn có thể nâng cấp lên trình biên dịch hỗ trợ các tham chiếu giá trị r (tôi tin là VS2010) và tạo các hàm tạo di chuyển. Nếu bạn không muốn làm điều đó thì bạn có thể sử dụng nhiều kỹ thuật phức tạp liên quan đến việc sử dụng trao đổi một cách có lương tâm hoặc thử thư viện Boost.Move.


không phải lúc nào cũng có thể xóa bộ nhớ được phân bổ trong hàm tạo. Làm thế nào để giải quyết tình huống này
Chris_vr

@Chris_vr Ý bạn là gì? Nếu tất cả các thành viên con trỏ của bạn là scope_ptrs và mỗi thành viên được khởi tạo riêng lẻ thì tất cả các thành viên được xây dựng thành công sẽ xóa con trỏ của chúng và những cái khác sẽ không giữ con trỏ vào bộ nhớ được phân bổ. Tôi sẽ đưa ra một ví dụ trong một vài giờ khi tôi đi làm về.
Mankude

@Chris_vr: nếu bạn có một ví dụ cụ thể, hãy đăng nó dưới dạng câu hỏi mới, để chúng tôi có thể thảo luận về vấn đề đó.
Doc Brown

5

Bạn có thể sử dụng công cụ Valgrind để phát hiện rò rỉ bộ nhớ.

Ngoài ra, để tìm rò rỉ trong một chức năng cụ thể, hãy sử dụng exit (0) ở cuối chức năng và sau đó chạy nó với Valgrind

`$` valgrind ./your_CPP_program 

5

Một cuộc khảo sát của kiểm tra rò rỉ bộ nhớ tự động

Trong câu trả lời này, tôi so sánh một số trình kiểm tra rò rỉ bộ nhớ khác nhau trong một ví dụ đơn giản về rò rỉ bộ nhớ.

Trước bất cứ điều gì, hãy xem bảng khổng lồ này trong wiki ASan so sánh tất cả các công cụ được biết đến với con người: https://github.com/google/sanitulators/wiki/AddressSanitizerComparisonOfMemoryTools/d06210f759fec97066888e5f27c7228

Ví dụ được phân tích sẽ là:

C chính

#include <stdlib.h>

void * my_malloc(size_t n) {
    return malloc(n);
}

void leaky(size_t n, int do_leak) {
    void *p = my_malloc(n);
    if (!do_leak) {
        free(p);
    }
}

int main(void) {
    leaky(0x10, 0);
    leaky(0x10, 1);
    leaky(0x100, 0);
    leaky(0x100, 1);
    leaky(0x1000, 0);
    leaky(0x1000, 1);
}

GitHub ngược dòng .

Chúng tôi sẽ cố gắng xem các công cụ khác nhau chỉ rõ chúng tôi đến các cuộc gọi bị rò rỉ như thế nào.

tcmalloc từ gperftools của Google

https://github.com/gperftools/gperftools

Cách sử dụng trên Ubuntu 19.04:

sudo apt-get install google-perftools
gcc -ggdb3 -o main.out main.c -ltcmalloc
PPROF_PATH=/usr/bin/google-pprof \
  HEAPCHECK=normal \
  HEAPPROFILE=ble \
  ./main.out \
;
google-pprof main.out ble.0001.heap --text

Đầu ra của chương trình chạy chứa phân tích rò rỉ bộ nhớ:

WARNING: Perftools heap leak checker is active -- Performance may suffer
Starting tracking the heap
Dumping heap profile to ble.0001.heap (Exiting, 4 kB in use)
Have memory regions w/o callers: might report false leaks
Leak check _main_ detected leaks of 272 bytes in 2 objects
The 2 largest leaks:
Using local file ./main.out.
Leak of 256 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581d3 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start
Leak of 16 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581b5 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start


If the preceding stack traces are not enough to find the leaks, try running THIS shell command:

pprof ./main.out "/tmp/main.out.24744._main_-end.heap" --inuse_objects --lines --heapcheck  --edgefraction=1e-10 --nodefraction=1e-10 --gv

If you are still puzzled about why the leaks are there, try rerunning this program with HEAP_CHECK_TEST_POINTER_ALIGNMENT=1 and/or with HEAP_CHECK_MAX_POINTER_OFFSET=-1
If the leak report occurs in a small fraction of runs, try running with TCMALLOC_MAX_FREE_QUEUE_SIZE of few hundred MB or with TCMALLOC_RECLAIM_MEMORY=false, it might help find leaks more re
Exiting with error code (instead of crashing) because of whole-program memory leaks

và đầu ra của google-pprofphân tích sử dụng heap:

Using local file main.out.
Using local file ble.0001.heap.
Total: 0.0 MB
     0.0 100.0% 100.0%      0.0 100.0% my_malloc
     0.0   0.0% 100.0%      0.0 100.0% __libc_start_main
     0.0   0.0% 100.0%      0.0 100.0% _start
     0.0   0.0% 100.0%      0.0 100.0% leaky
     0.0   0.0% 100.0%      0.0 100.0% main

Đầu ra chỉ cho chúng tôi hai trong số ba rò rỉ:

Leak of 256 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581d3 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start
Leak of 16 bytes in 1 objects allocated from:
        @ 555bf6e5815d my_malloc
        @ 555bf6e5817a leaky
        @ 555bf6e581b5 main
        @ 7f71e88c9b6b __libc_start_main
        @ 555bf6e5808a _start

Tôi không chắc tại sao cái thứ ba không xuất hiện

Trong mọi trường hợp, khi thông thường khi một cái gì đó bị rò rỉ, nó xảy ra rất nhiều lần và khi tôi sử dụng nó trong một dự án thực sự, tôi cuối cùng đã được chỉ ra chức năng rò rỉ rất dễ dàng.

Như đã đề cập trên chính đầu ra, điều này phát sinh sự chậm thực hiện đáng kể.

Tài liệu khác tại:

Xem thêm: Làm thế nào để sử dụng TCMalloc?

Đã thử nghiệm trong Ubuntu 19.04, google-perftools 2.5-2.

Địa chỉ vệ sinh (ASan) cũng bởi Google

https://github.com/google/sanitators

Được đề cập trước đây tại: Làm thế nào để tìm rò rỉ bộ nhớ trong mã / dự án C ++? TODO vs tcmalloc.

Điều này đã được tích hợp vào GCC, vì vậy bạn chỉ cần làm:

gcc -fsanitize=address -ggdb3 -o main.out main.c
./main.out 

và thực hiện đầu ra:

=================================================================
==27223==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 4096 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f210 in main /home/ciro/test/main.c:20
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

Direct leak of 256 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f1f2 in main /home/ciro/test/main.c:18
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

Direct leak of 16 byte(s) in 1 object(s) allocated from:
    #0 0x7fabbefc5448 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c448)
    #1 0x55bf86c5f17c in my_malloc /home/ciro/test/main.c:4
    #2 0x55bf86c5f199 in leaky /home/ciro/test/main.c:8
    #3 0x55bf86c5f1d4 in main /home/ciro/test/main.c:16
    #4 0x7fabbecf4b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)

SUMMARY: AddressSanitizer: 4368 byte(s) leaked in 3 allocation(s).

trong đó xác định rõ tất cả các rò rỉ. Đẹp!

ASan cũng có thể thực hiện các kiểm tra thú vị khác như viết ngoài lề: Phát hiện đập vỡ ngăn xếp

Đã thử nghiệm trong Ubuntu 19.04, GCC 8.3.0.

Valgrind

http://www.valgrind.org/

Được đề cập trước đây tại: https://stackoverflow.com/a/37661630/895245

Sử dụng:

sudo apt-get install valgrind
gcc -ggdb3 -o main.out main.c
valgrind --leak-check=yes ./main.out

Đầu ra:

==32178== Memcheck, a memory error detector
==32178== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==32178== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==32178== Command: ./main.out
==32178== 
==32178== 
==32178== HEAP SUMMARY:
==32178==     in use at exit: 4,368 bytes in 3 blocks
==32178==   total heap usage: 6 allocs, 3 frees, 8,736 bytes allocated
==32178== 
==32178== 16 bytes in 1 blocks are definitely lost in loss record 1 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091B4: main (main.c:16)
==32178== 
==32178== 256 bytes in 1 blocks are definitely lost in loss record 2 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091D2: main (main.c:18)
==32178== 
==32178== 4,096 bytes in 1 blocks are definitely lost in loss record 3 of 3
==32178==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==32178==    by 0x10915C: my_malloc (main.c:4)
==32178==    by 0x109179: leaky (main.c:8)
==32178==    by 0x1091F0: main (main.c:20)
==32178== 
==32178== LEAK SUMMARY:
==32178==    definitely lost: 4,368 bytes in 3 blocks
==32178==    indirectly lost: 0 bytes in 0 blocks
==32178==      possibly lost: 0 bytes in 0 blocks
==32178==    still reachable: 0 bytes in 0 blocks
==32178==         suppressed: 0 bytes in 0 blocks
==32178== 
==32178== For counts of detected and suppressed errors, rerun with: -v
==32178== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

Vì vậy, một lần nữa, tất cả các rò rỉ đã được phát hiện.

Xem thêm: Làm cách nào để sử dụng valgrind để tìm rò rỉ bộ nhớ?

Đã thử nghiệm trong Ubuntu 19.04, valgrind 3.14.0.


4

Visual Leak dò (VLD) là một hệ thống phát hiện rò rỉ bộ nhớ nguồn mở, miễn phí, mạnh mẽ cho Visual C ++.

Khi bạn chạy chương trình của mình trong trình gỡ lỗi Visual Studio, Trình phát hiện rò rỉ Visual sẽ xuất báo cáo rò rỉ bộ nhớ vào cuối phiên gỡ lỗi của bạn. Báo cáo rò rỉ bao gồm ngăn xếp cuộc gọi đầy đủ cho thấy bất kỳ khối bộ nhớ bị rò rỉ nào được phân bổ. Nhấp đúp vào một dòng trong ngăn xếp cuộc gọi để chuyển đến tập tin đó và dòng trong cửa sổ soạn thảo.

Nếu bạn chỉ có các bãi đổ vỡ, bạn có thể sử dụng !heap -llệnh Windbg , nó sẽ phát hiện các khối heap bị rò rỉ. Tốt hơn nên mở tùy chọn gflags: Tạo Tạo cơ sở dữ liệu theo dõi ngăn xếp chế độ người dùng, sau đó bạn sẽ thấy ngăn xếp cuộc gọi cấp phát bộ nhớ.


4

MTuner là một công cụ phân tích, phát hiện và phân tích bộ nhớ đa nền tảng miễn phí hỗ trợ các trình biên dịch MSVC, GCC và Clang. Các tính năng bao gồm:

  • lịch sử dựa trên dòng thời gian sử dụng bộ nhớ và khối bộ nhớ trực tiếp
  • lọc hoạt động bộ nhớ mạnh mẽ dựa trên heap, thẻ nhớ, phạm vi thời gian, v.v.
  • SDK cho thiết bị thủ công với mã nguồn đầy đủ
  • hỗ trợ tích hợp liên tục thông qua việc sử dụng dòng lệnh
  • gọi ngăn xếp cây và điều hướng bản đồ cây
  • nhiều hơn nữa

Người dùng có thể cấu hình bất kỳ nền tảng nhắm mục tiêu phần mềm nào với trình biên dịch chéo GCC hoặc Clang. MTuner đi kèm với sự hỗ trợ tích hợp cho các nền tảng Windows, PlayStation 4 và PlayStation 3.


Đây phải là câu trả lời được chấp nhận. Đây là một công cụ tuyệt vời và có thể xử lý khối lượng phân bổ / thỏa thuận mà người khác không thể.
Serge Rogatch

3

Trên Windows, bạn có thể sử dụng CRT gỡ lỗi heap .

Có bất kỳ tiêu chuẩn hoặc thủ tục nào nên tuân theo để đảm bảo không có rò rỉ bộ nhớ trong chương trình.

Vâng, không sử dụng quản lý bộ nhớ thủ công (nếu bạn từng gọi deletehoặc delete[]thủ công, thì bạn đã làm sai). Sử dụng RAII và con trỏ thông minh, giới hạn phân bổ heap đến mức tối thiểu tuyệt đối (hầu hết thời gian, các biến tự động sẽ đủ).


3

Trả lời phần thứ hai của câu hỏi của bạn,

Có bất kỳ tiêu chuẩn hoặc thủ tục nào nên tuân theo để đảm bảo không có rò rỉ bộ nhớ trong chương trình.

Có, có. Và đây là một trong những khác biệt chính giữa C và C ++.

Trong C ++, bạn không bao giờ nên gọi newhoặc deletetrong mã người dùng của mình. RAII là một kỹ thuật được sử dụng rất phổ biến, giải quyết được khá nhiều vấn đề quản lý tài nguyên. Mọi tài nguyên trong chương trình của bạn (tài nguyên là bất cứ thứ gì phải có, và sau đó, được phát hành: xử lý tệp, ổ cắm mạng, kết nối cơ sở dữ liệu, nhưng cũng phân bổ bộ nhớ đơn giản và trong một số trường hợp, các cặp lệnh gọi API (BeginX ( ) / EndX (), LockY (), UnlockY ()), nên được gói trong một lớp, trong đó:

  • hàm tạo có được tài nguyên (bằng cách gọi newnếu tài nguyên là phân bổ memroy)
  • hàm hủy giải phóng tài nguyên,
  • sao chép và gán được ngăn chặn (bằng cách đặt hàm tạo sao chép và toán tử gán) hoặc được triển khai để hoạt động chính xác (ví dụ: bằng cách sao chép tài nguyên bên dưới)

Lớp này sau đó được khởi tạo cục bộ, trên ngăn xếp hoặc với tư cách là thành viên của lớp chứ không phải bằng cách gọi newvà lưu trữ một con trỏ.

Bạn thường không cần phải tự xác định các lớp này. Các thùng chứa thư viện tiêu chuẩn cũng hoạt động theo cách này, sao cho mọi đối tượng được lưu trữ vào std::vectorsẽ được giải phóng khi vectơ bị phá hủy. Vì vậy, một lần nữa, không lưu trữ một con trỏ vào vùng chứa (sẽ yêu cầu bạn gọi newdelete), mà thay vào đó là chính đối tượng (cung cấp cho bạn quản lý bộ nhớ miễn phí ). Tương tự như vậy, các lớp con trỏ thông minh có thể được sử dụng để dễ dàng bao bọc các đối tượng chỉ cần được phân bổ newvà kiểm soát thời gian sống của chúng.

Điều này có nghĩa là khi đối tượng đi ra khỏi phạm vi, nó sẽ tự động bị phá hủy và tài nguyên của nó được giải phóng và dọn sạch.

Nếu bạn làm điều này một cách nhất quán trong suốt mã của mình, bạn sẽ không bị rò rỉ bộ nhớ. Mọi thứ có thể bị rò rỉ đều được gắn với một hàm hủy được đảm bảo được gọi khi điều khiển rời khỏi phạm vi mà đối tượng được khai báo.


nếu con trỏ thông minh giữ một lớp và lớp đó chứa con trỏ của một vài lớp khác. khi thông minh tắt có nghĩa là tất cả các con trỏ bên trong sẽ bị xóa an toàn.
Chris_vr

@Chris: Giả sử rằng đối tượng được trỏ bởi con trỏ thông minh có một hàm hủy có chức năng dọn dẹp cần thiết hoặc đối tượng chứa các thành viên mà chính chúng có các hàm hủy để thực hiện việc dọn dẹp cần thiết. Về bản chất, miễn là mọi đối tượng tự chăm sóc bản thân (tự dọn dẹp khi nó bị phá hủy) và miễn là mọi đối tượng được lưu trữ theo giá trị, không phải là một con trỏ, thì mọi thứ cần được giải phóng sẽ được giải phóng.
jalf

3

AddressSanitizer (ASan) là một trình phát hiện lỗi bộ nhớ nhanh. Nó tìm thấy lỗi sử dụng sau khi sử dụng miễn phí và {heap, stack, global} -buffer trong các chương trình C / C ++. Nó tìm thấy:

  • Sử dụng sau khi miễn phí (lơ lửng con trỏ)
  • Heap đệm tràn
  • Tràn bộ đệm
  • Tràn bộ đệm toàn cầu
  • Sử dụng sau khi trở về
  • Lỗi khởi tạo

Công cụ này rất nhanh. Độ chậm trung bình của chương trình thiết bị là ~ 2x.


Đặc biệt là xem LeakSanitizer
Gabriel Devillers

0

Ngoài các công cụ và phương thức được cung cấp trong các anwers khác, các công cụ phân tích mã tĩnh có thể được sử dụng để phát hiện rò rỉ bộ nhớ (và các vấn đề khác). Một công cụ mạnh mẽ miễn phí là Cppcheck. Nhưng có rất nhiều công cụ khác có sẵn. Wikipedia có một danh sách các công cụ phân tích mã tĩnh.


-1

Hãy chắc chắn rằng tất cả bộ nhớ heap được giải phóng thành công. Không có nhu cầu nếu bạn không bao giờ phân bổ bộ nhớ trên heap. Nếu bạn làm như vậy, hãy đếm số lần bạn sử dụng bộ nhớ malloc và đếm số lần bạn giải phóng bộ nhớ.


-3

Không nên sử dụng "mới" hoặc "xóa" trong mã ứng dụng. Thay vào đó, hãy tạo một kiểu mới sử dụng thành ngữ manager / worker, trong đó lớp manager để phân bổ và giải phóng bộ nhớ và chuyển tiếp tất cả các hoạt động khác cho đối tượng worker.

Thật không may, đây là công việc nhiều hơn nó nên vì C ++ không quá tải "toán tử". Nó thậm chí còn làm việc nhiều hơn với sự hiện diện của đa hình.

Nhưng điều này đáng để nỗ lực vì sau đó bạn không bao giờ phải lo lắng về việc rò rỉ bộ nhớ, điều đó có nghĩa là bạn thậm chí không phải tìm kiếm chúng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.