我是Windows平台上的c++程序员。我使用的是Visual Studio 2008。

我通常会在代码中出现内存泄漏。

通常我通过检查代码来发现内存泄漏,但这很麻烦,而且并不总是一种好方法。

因为我买不起付费的内存泄漏检测工具,所以我想让你们建议避免内存泄漏的最佳方法。

我想知道如何程序员可以找到内存泄漏。 是否应该遵循某种标准或过程来确保程序中没有内存泄漏?


当前回答

确保成功释放了所有堆内存。如果您从未在堆上分配内存,则不需要这样做。如果你这样做,计算你malloc内存的次数,并计算你释放内存的次数。

其他回答

如果使用gcc,则有gprof可用。

我想知道程序员如何发现内存泄漏

有些人使用工具,有些人做你做的事情,也可以通过同行代码审查

是否应该遵循某种标准或过程来确保程序中没有内存泄漏

对我来说:每当我创建动态分配对象时,我总是把释放代码放在后面,然后在中间填充代码。如果您确定之间的代码中不会出现异常,那么这样做是可以的。否则,我会使用try-finally(我不经常使用c++)。

Search your code for occurrences of new, and make sure that they all occur within a constructor with a matching delete in a destructor. Make sure that this is the only possibly throwing operation in that constructor. A simple way to do this is to wrap all pointers in std::auto_ptr, or boost::scoped_ptr (depending on whether or not you need move semantics). For all future code just ensure that every resource is owned by an object that cleans up the resource in its destructor. If you need move semantics then you can upgrade to a compiler that supports r-value references (VS2010 does I believe) and create move constructors. If you don't want to do that then you can use a variety of tricky techniques involving conscientious usage of swap, or try the Boost.Move library.

自动内存泄漏检查器的概述

在这个回答中,我在一个简单易懂的内存泄漏示例中比较了几种不同的内存泄漏检查程序。

在做任何事情之前,请参阅ASan wiki中比较人类已知所有工具的巨大表格:https://github.com/google/sanitizers/wiki/AddressSanitizerComparisonOfMemoryTools/d06210f759fec97066888e5f27c7e722832b0924

分析的例子如下:

c

#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上游。

我们将尝试看看不同的工具如何清楚地向我们指出泄漏的调用。

从gperftools通过谷歌

https://github.com/gperftools/gperftools

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

程序运行的输出包含内存泄漏分析:

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

google-pprof的输出包含堆使用分析:

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

输出为我们指出了三个泄漏中的两个:

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

我不知道为什么第三个没出现

在任何情况下,通常当某些东西泄漏时,它会发生很多次,当我在一个真实的项目中使用它时,我只是很容易就被指出泄漏函数。

正如输出本身所提到的,这会导致显著的执行速度放缓。

进一步文件载于:

https://gperftools.github.io/gperftools/heap_checker.html https://gperftools.github.io/gperftools/heapprofile.html

参见:如何使用TCMalloc?

在Ubuntu 19.04中测试,google-perftools 2.5-2。

地址消毒(ASan)也由谷歌

https://github.com/google/sanitizers

前面提到过:如何在c++代码/项目中找到内存泄漏?TODO vs tcmalloc。

这已经集成到GCC中,所以你可以这样做:

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

和执行输出:

=================================================================
==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).

它清楚地识别所有泄漏。好了!

ASan还可以做其他很酷的检查,比如越界写入:检测到堆栈破坏

在Ubuntu 19.04, GCC 8.3.0中测试。

Valgrind

http://www.valgrind.org/

之前提到过:https://stackoverflow.com/a/37661630/895245

用法:

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

输出:

==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)

所以再一次,所有的泄漏都被检测到了。

请参见:如何使用valgrind查找内存泄漏?

在Ubuntu 19.04, valgrind 3.14.0中测试。

确保成功释放了所有堆内存。如果您从未在堆上分配内存,则不需要这样做。如果你这样做,计算你malloc内存的次数,并计算你释放内存的次数。

在Windows上,你可以使用CRT调试堆。

是否有任何标准或程序可以确保程序中没有内存泄漏。

是的,不要使用手动内存管理(如果你曾经手动调用delete或delete[],那么你做错了)。使用RAII和智能指针,将堆分配限制到绝对最小值(大多数时候,自动变量就足够了)。