Introduction
In this article I will discuss a way to build a memory leak detection program for C and C++ applications.
This is not an attempt at writing a fully fledged leak detector but rather an introduction to one way (of many) ways of finding leaks.
The approach I've gone for is library injection and even if the C++ source code provided is for Linux (tested on Ubuntu 11.10) the method should work for any platform that allows library injection.
What is a leak
There are many ways in which an application can leak resources, and there are many different resources that can be leaked. In this article I'll focus on and only look for memory leaks (not file handles or sockets or any other resource that might be allocated), but even when constraining the scope to just memory there are still many different kinds of leaks. When talking about memory leaks in C++, most people think of scenarios like this (very simplified one);
Collapse | Copy Code
void foo()
{
int* my_pointer = new int;
} // my_pointer is never deleted, we've just leaked sizeof(int) bytes!
Memory is allocated but is not deleted/unallocated before the reference to the allocated memory goes out of scope. This means of course that the memory will be unavailable for use as well as for further allocations until the program terminates.
But memory leaks can appear in other more subtle ways as well, sometimes the memory is still referenced but just not used, such as when items are regularly added to a std::vector without ever being released. If such a std::vector is never cleared and still never again looked at by the application it can be considered leak, even though the memory is still referenced.
In this article, for simplicity, I'll only look at the first scenario; allocated memory that is not deallocated. In short, I'll show a way of tracking each call to malloc and free and record information so that they can be married up.
You might argue that you're using C++ and are therefore allocating and deallocating your memory using new and delete but in most cases the implementation of those will still call the C versions malloc and free.
Library Injection
Library Injection is when a user tells the OS to first look in LibraryA for system wide methods before looking in the "standard" libraries, where LibraryA is a library containing an overload of a system function. On some Linux and Unix distributions this can be achieved using the LD_PRELOAD environment variable to set the library that is to be injected.
To track memory allocations and deallocations a shared library containing overloads for malloc and free must be created and then injected so that whenever an allocation of memory is requested the custom version is hit first.
First step is to create a C++ file, leakfinder.cpp, that will contain the two methods;
Collapse | Copy Code
#include<iostream>
void* malloc(size_t size)
{
std::cout << "malloc called" << std::endl;
// Somehow track this allocation
}
void free(void* ptr)
{
std::cout << "free called" << std::endl;
// Somehow marry up this deallocation to a previous allocation
}
The above example can be compiled into a shared library using g++ (which is the compiler I'll use for the C++ code through out this article) by running:
Collapse | Copy Code
~/SomeFolder$g++ -shared -fPIC leakfinder.cpp -o leakfinder.so -ldl
Using an example C program that is built to leak we can test this, the example C program is called c_example and it looks like this:
Collapse | Copy Code
#include <stdlib.h>
#include <stdio.h>
void foo(int size)
{
int* data = malloc(sizeof(int) * size);
// Uncomment this to stop leak
//free(data);
}
void bar(int size)
{
char *data = malloc(sizeof(char) * size);
foo(size);
// Uncomment this to stop leak
//free(data);
}
void foobar(int size)
{
bar(size);
}
int main(void)
{
printf("leakfinder C example app\n");
printf("This application is expected to leak\n");
foobar(8);
foobar(16);
printf("leakfinder C example app all done\n");
return 0;
}
Important!
It is important to test the injection in a terminal other than the one used to compile the source code or the injection might (will) interfere with the compiler and any other commands that use malloc or free.
Therefore, when working with this example make sure you have one terminal open for compiling and building (both the leak finder and the example applictions), and one where the LD_PRELOAD environment variable is set where you can run the test applications.
Another way of making sure the pre-load only is applicable for the c_example application is by running it like this; LD_PRELOAD=./leakfinder.so c_example.
Set the LD_PRELOAD in your terminal to point to the leakfinder.so and run the c_example test application using;
Collapse | Copy Code
~/SomeFolder$export LD_PRELOAD=./leakfinder.so
Collapse | Copy Code
~/SomeFolder$./c_example
When first run it turns out that no allocations or deallocations are detected, this is because of the linkage. As g++ (a C++ and not a C compiled) was used to compile the leakfinder.cpp file it has applied name mangling and that means he function names do not match the intended system functions malloc and free. To resolve this issue the functions has to be declared with C linkage:
Collapse | Copy Code
#include<iostream>
extern "C" void* malloc(size_t size)
{
std::cout << "malloc called" << std::endl;
// Somehow track this allocation
}
extern "C" void free(void* ptr)
{
std::cout << "free called" << std::endl;
// Somehow marry up this deallocation to a previous allocation
}
When the leak finder with the correct linkage is preloaded and the example is run it will generate an output that looks something like this;
Loads and loads of allocations and deallocations intercepted!
This is because even though the test application only explicitly allocates a few things other parts also allocates and deallocates stuff, such as the printf and even the cout calls in the injected code.
The print out ends with a Segmentation fault which is of course because you cannot hope to replace an actual memory allocation with a print statement and expect stuff to still work " /> , but the method is proved to work.
Actual implementation
As it is clearly very important for the overloaded methods malloc and free to still do the work they are intended to do (as in allocating and deallocating memory on and from the heap) the injected code must look up pointers to the actual implementation and delegate the calls to these before tracking them.
Finding it
To find the actual implementation is done by calling dlsym (defined in dlfcn.h), this function returns function pointers to functions made available using dlopen. Using the constant RTLD_NEXT the previous entry can be retrieved, and that pointer can be stored in a static variable to cache it so the lookup doesn't have to take place every time;
Collapse | Copy Code
static void* (*sys_malloc)(size_t) = 0;
static void (*sys_free)(void*) = 0;
static void initialize_functions(void)
{
sys_malloc = reinterpret_cast<void*(*)(size_t)>(dlsym(RTLD_NEXT, "malloc"));
if (sys_malloc == 0)
cerr << "leakfinder failed to read malloc function; " << dlerror() << endl;
sys_free = reinterpret_cast<void(*)(void*)>(dlsym(RTLD_NEXT, "free"));
if (sys_free == 0)
cerr << "leakfinder failed to read free function; " << dlerror() << endl;
}
extern "C" void* malloc(size_t size)
{
cout << "malloc called" << endl;
if (sys_malloc == 0)
initialize_functions();
void* ptr = sys_malloc(size);
return ptr;
}
Collapse | Copy Code
extern "C" void free(void* ptr)
{
cout << "free called" << endl;
if (sys_free == 0)
initialize_functions();
sys_free(ptr);
}
In the code above the system version (or standard version) of malloc and free are looked up using dlsym(RTLD_NEXT, [name of function]) and then stored in sys_malloc and sys_free respectively.
After the system version of the functions are cached (if not already cached) they're called to perform the allocation or deallocation and after that the leak finder will intercept the calls to track the information required to compile a list of leaks.
Allocation Info
To allow the user to fix leaks, each leak needs to be associated with additional information to make it easy to track down the leak and correct it, in leakfinder I track four different things;
References
Stacktrace
Size
Thread
References
In order to marry up a deallocation to a previous allocation there need to be something that is unique and consistent across malloc and free calls, and one thing that can be used is the address of the allocated memory. It has to be unique as no two allocations are allowed to get the same piece of memory and it's consistent across calls as it's the return value of the allocation and the parameter to the deallocation.
Stacktrace
In order for a memory leak detector to be useful it needs to be able to tell the user where the leak was allocated. One way to get a stacktrace in Linux is to use the backtrace and backtrace_symbols functions in execinfo.h.
Function backtrace gets all the return addresses for all the functions that are currently active on the stack in a particular thread, which is essentially the stacktrace.
In order to get a more readable version of the stacktrace the return addresses from backtrace can be fed into backtrace_symbols to get the names of the functions on the stack.
Collapse | Copy Code
void* frames[max_frame_depth];
size_t stack_size = backtrace(frames, max_frame_depth);
char** stacktrace = backtrace_symbols(frames, stack_size);
Size and Thread
Less important but still sometimes useful is to record the size of the allocation and which thread it was allocated on.
The size is passed in as a size_t argument to malloc so that is easy enough to grab and record.
Recording the current thread id might be slightly harder depending on the thread library used, in this example I've used the pthread library so I get the thread id as a pthread_t by calling pthread_self().
allocation_info
The information listed above, reference, stacktrace, size and thread are in leakfinder stored in a class called allocation_info.
allocation_info.hpp
Collapse | Copy Code
#ifndef __allocation_info
#define __allocation_info
#include <vector>
#include <string>
#include <pthread.h>
namespace bornander
{
namespace memory
{
class allocation_info
{
public:
typedef long long address_type;
private:
allocation_info::address_type address;
size_t size;
std::vector<std::string> stacktrace;
pthread_t thread_id;
public:
allocation_info(void* address, size_t size, char** stacktrace, size_t depth, pthread_t thread_id);
allocation_info::address_type get_address() const;
size_t get_size() const;
std::vector<std::string> get_stacktrace() const;
pthread_t get_thread_id() const;
};
}
}
#endif
allocation_info.cpp
Collapse | Copy Code
#include "allocation_info.hpp"
namespace bornander
{
namespace memory
{
allocation_info::allocation_info(void* address, size_t size, char** stacktrace, size_t depth, pthread_t thread_id)
{
this->address = reinterpret_cast<allocation_info::address_type>(address);
this->size = size;
this->thread_id = thread_id;
// Skip first frame as that is the overriden malloc method
for(int i = 1; i < depth; ++i)
{
std::string frame = stacktrace[i];
this->stacktrace.push_back(frame);
}
}
allocation_info::address_type allocation_info::get_address() const
{
return address;
}
size_t allocation_info::get_size() const
{
return size;
}
std::vector<std::string> allocation_info::get_stacktrace() const
{
return stacktrace;
}
pthread_t allocation_info::get_thread_id() const
{
return thread_id;
}
}
}
Tracking leaks
Armed with a way of intercepting allocations (library injection) and a way to store allocation information (allocation_info) we're now ready to implement our basic memory leak detector.
Allocations
The first problem with tracking allocations inside the allocation method is that to track it memory needs to be allocated to store the allocation_info, and that obviously means that for every allocation another allocation is required. Since the additional allocation uses malloc as well the approach leads to a stack overflow.
The solution is to only track allocations that are originating from outside of leakfinder. By declaring a static boolean called isExternalSource that is set to false before the allocation is recorded and back to true when done it is possible to exclude allocation that arise from recording the source allocation. The overloaded malloc method then looks something like this;
Collapse | Copy Code
static bool isExternalSource = true;
static void* (*sys_malloc)(size_t) = 0;
extern "C" void* malloc(size_t size)
{
// Make sure we're initialized
if (sys_malloc == 0)
initialize_functions();
Collapse | Copy Code
// Call the actual malloc and keep the result
Collapse | Copy Code
void* ptr = sys_malloc(size);
if (isExternalSource)
{
isExternalSource = false;
// Record the details of this allocation in an allocation_info
isExternalSource = true;
}
return ptr;
}
This takes care of the exclusion of internal allocation but suffers from threading issues as the static isExternalSource might be read/written by two threads at the same time causing undefined behaviour.
By guarding the inside of the if-statement with a lock (using pthread threads) the malloc method changes to this;
Collapse | Copy Code
static pthread_mutex_t cs_mutex = PTHREAD_MUTEX_INITIALIZER;
static bool isExternalSource = true;
static void* (*sys_malloc)(size_t) = 0;
extern "C" void* malloc(size_t size)
{
// Make sure we're initialized
if (sys_malloc == 0)
initialize_functions();
// Call the actual malloc and keep the result
void* ptr = sys_malloc(size);
if (isExternalSource)
{
pthread_t thread_id = pthread_self();
pthread_mutex_lock(&cs_mutex);
isExternalSource = false;
// Record the allocation here
isExternalSource = true;
pthread_mutex_unlock(&cs_mutex);
}
return ptr;
}
Now the malloc implementation is thread safe (or thread-safeish, it still suffers from some issues but for sake of simplicity I'm going to keep it this way for this article).
The rest of the implementation is the matter of grabbing the stacktrace and storing it along with the reference, size and thread id. The size is already passed in and the thread id has been grabbed using pthread_self, the remaining this to store is simply the reference which is the address of ptr which is returned by the actual implementation of malloc.
All of the above yields a malloc function that looks like this;
Collapse | Copy Code
static pthread_mutex_t cs_mutex = PTHREAD_MUTEX_INITIALIZER;
static size_t allocation_count = 0;
static vector<allocation_info> allocations;
static const size_t max_frame_depth = 128;
static bool isExternalSource = true;
static void* (*sys_malloc)(size_t) = 0;
extern "C" void* malloc(size_t size)
{
if (sys_malloc == 0)
initialize_functions();
void* ptr = sys_malloc(size);
if (isExternalSource)
{
pthread_t thread_id = pthread_self();
pthread_mutex_lock(&cs_mutex);
isExternalSource = false;
// Used for summary statistics
++allocation_count;
// Grab stacktrace
void* frames[max_frame_depth];
size_t stack_size = backtrace(frames, max_frame_depth);
char** stacktrace = backtrace_symbols(frames, stack_size);
allocation_info allocation(ptr, size, stacktrace, stack_size, thread_id);
allocations.push_back(allocation);
// Make sure to release the memory allocated by backtrace_symbols
sys_free(stacktrace);
Collapse | Copy Code
isExternalSource = true;
pthread_mutex_unlock(&cs_mutex);
}
return ptr;
}
Deallocations
To marry up a deallocation to a previous allocation is simple. The free uses the same method of preventing it to be run for an internal free and thread safety and in addition to this it's just a matter of finding the allocation_info in the allocations vector. The unique key is the reference or address;
Collapse | Copy Code
extern "C" void free(void* ptr)
{
if (sys_free == 0)
initialize_functions();
allocation_info::address_type address = reinterpret_cast<allocation_info::address_type>(ptr);
sys_free(ptr);
if (isExternalSource)
{
pthread_mutex_lock(&cs_mutex);
isExternalSource = false;
for (int i = 0; i < allocations.size(); ++i)
{
allocation_info allocation = allocations[i];
if (allocation.get_address() == address)
{
allocations.erase(allocations.begin() + i);
break;
}
}
isExternalSource = true;
pthread_mutex_unlock(&cs_mutex);
}
}
Summing it up
After all the allocations and deallocations have been tracked and matched off the ones that were never deallocated must be reported on, so when is a suitable time to do that?
Since it's pretty much technically impossible to tell if a leak has happened before the program terminates leakfinder uses program exit to sum up the leaks. One might argue that the leak happens when the pointer referencing the memory goes out of scope of the pointer has not been deallocated but since it is possible to store the pointer value in just about any other data structure that can be hard to rely on.
C style destructor
There are different ways to hook into the termination of a program, which one to pick depends on platform and personal taste.
One approach is to use a pragma directive;
Collapse | Copy Code
#pragma fini (some_exit_handler)
but for leakfinder I've gone for a C style destructor;
Collapse | Copy Code
static void compile_allocation() __attribute__((destructor));
Using this approach, the method compile_allocation is executed when the shared library is unloaded, this is typically at program exit.
Since all the not unallocated allocations are held in the vector allocations at program exit, the work of the compile_allocation method is just to iterate through the leaks and somehow output the leak information.
Where the best place to output the leak information to I am not entirely sure of. In certain scenarios a file would be convinient but for simplicity I've decided to let leakfinder just dump the summary to standard out.
To avoid extra work isExternalSource needs to be set to false at the beginning of compile_allocaion as printing the summary requires allocations to take place.
To print addresses and pointers in hex variables hex and dec from iomanip are used.
Collapse | Copy Code
void compile_allocation()
{
isExternalSource = false;
if (allocations.empty())
{
cout << "leakfinder found no leaks, not one of the " << allocation_count;
cout << " allocations was not released." << endl;
}
else
{
cout << "leakfinder detected that " << allocations.size();
cout << " out of " << allocation_count << " allocations was not released." << endl;
for (int i = 0; i < allocations.size(); ++i)
{
allocation_info allocation = allocations[i];
cout << "Leak " << (i+1) << "@0x" << hex << allocation.get_thread_id() << dec;
cout << "; leaked " << allocation.get_size() << " bytes at position 0x";
cout << hex << allocation.get_address() << dec << endl;
vector<string> stacktrace = allocation.get_stacktrace();
for (int j = 0; j < stacktrace.size(); ++j)
{
cout << "\t" << stacktrace[j] << endl;
}
}
}
}
Trying it out
To try it out first build the leakfinder shared library by typing (there's also a makefile included for those who cares not for compiling by hand);
Collapse | Copy Code
g++ -shared -fPIC allocation_info.cpp leakfinder.cpp -o leakfinder.so -lpthread -ldl
Then build the c_example shared library by typing;
Collapse | Copy Code
cc c_example.c -o c_example
Then, open up a different terminal and set the LD_PRELOAD;
Collapse | Copy Code
export LD_PRELOAD=./leakfinder.so
Lastly, run the c_example in the terminal where LD_PRELOAD was set;
Collapse | Copy Code
./c_example
Running that application should produce an output similar to this;
Collapse | Copy Code
fredrik@ubuntu-01:~/Development/leakfinder$ ./c_example
leakfinder C example app
This application is expected to leak
leakfinder C example app all done
leakfinder detected that 4 out of 4 allocations was not released.
Leak 1@0xb77876d0; leaked 8 bytes at position 0x8e63020
./c_example() [0x804843e]
./c_example() [0x804845f]
./c_example() [0x804848e]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xaef113]
./c_example() [0x8048381]
Leak 2@0xb77876d0; leaked 32 bytes at position 0x8e632d0
./c_example() [0x8048428]
./c_example() [0x804844c]
./c_example() [0x804845f]
./c_example() [0x804848e]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xaef113]
./c_example() [0x8048381]
Leak 3@0xb77876d0; leaked 16 bytes at position 0x8e63230
./c_example() [0x804843e]
./c_example() [0x804845f]
./c_example() [0x804849a]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xaef113]
./c_example() [0x8048381]
Leak 4@0xb77876d0; leaked 64 bytes at position 0x8e63318
./c_example() [0x8048428]
./c_example() [0x804844c]
./c_example() [0x804845f]
./c_example() [0x804849a]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xaef113]
./c_example() [0x8048381]
fredrik@ubuntu-01:~/Development/leakfinder$
While that has indeed first printed the output of c_example followed by the summary of the four leaks, the stactrace isn't very easy to dechiper. That is because the program is lacking symbols but by compiling it again (in the first terminal) with option -rdynamic this can be corrected;
Collapse | Copy Code
cc -rdynamic c_example.c -o c_example
A more useful stacktrace is provided;
Collapse | Copy Code
fredrik@ubuntu-01:~/Development/leakfinder$ ./c_example
leakfinder C example app
This application is expected to leak
leakfinder C example app all done
leakfinder detected that 4 out of 4 allocations was not released.
Leak 1@0xb76fb6d0; leaked 8 bytes at position 0x91d9020
./c_example(bar+0x11) [0x804860e]
./c_example(foobar+0x11) [0x804862f]
./c_example(main+0x2d) [0x804865e]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x144113]
./c_example() [0x8048551]
Leak 2@0xb76fb6d0; leaked 32 bytes at position 0x91d92f8
./c_example(foo+0x14) [0x80485f8]
./c_example(bar+0x1f) [0x804861c]
./c_example(foobar+0x11) [0x804862f]
./c_example(main+0x2d) [0x804865e]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x144113]
./c_example() [0x8048551]
Leak 3@0xb76fb6d0; leaked 16 bytes at position 0x91d9258
./c_example(bar+0x11) [0x804860e]
./c_example(foobar+0x11) [0x804862f]
./c_example(main+0x39) [0x804866a]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x144113]
./c_example() [0x8048551]
Leak 4@0xb76fb6d0; leaked 64 bytes at position 0x91d9340
./c_example(foo+0x14) [0x80485f8]
./c_example(bar+0x1f) [0x804861c]
./c_example(foobar+0x11) [0x804862f]
./c_example(main+0x39) [0x804866a]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x144113]
./c_example() [0x8048551]
fredrik@ubuntu-01:~/Development/leakfinder$
Now the method name is included and that makes it a whole lot easier to read, and while this works for C++ programs as well their stacktraces are still a bit messy because of name mangling, but still very much readable when compiled with -rdynamic, as seen below in the output from cpp_example (also included in the download);
Collapse | Copy Code
fredrik@ubuntu-01:~/Development/leakfinder$ ./cpp_example
leakfinder C++ thread example app
This application is expected to leak
leakfinder detected that 4 out of 5 allocations was not released.
Leak 1@0xb77e66d0; leaked 4 bytes at position 0x8e79020
/usr/lib/i386-linux-gnu/libstdc++.so.6(_Znwj+0x27) [0x2f19d7]
./cpp_example(_ZN10my_class_aC1Ev+0x12) [0x8048c64]
./cpp_example(_ZN10my_class_bC1Ev+0x11) [0x8048ce9]
./cpp_example(main+0x5f) [0x8048b97]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x346113]
./cpp_example() [0x8048a81]
Leak 2@0xb77e66d0; leaked 4 bytes at position 0x8e79108
/usr/lib/i386-linux-gnu/libstdc++.so.6(_Znwj+0x27) [0x2f19d7]
./cpp_example(_ZN10my_class_b3fooEv+0x12) [0x8048caa]
./cpp_example(_ZN10my_class_b3barEv+0x11) [0x8048cc1]
./cpp_example(_ZN10my_class_b6foobarEv+0x11) [0x8048cd5]
./cpp_example(main+0x6b) [0x8048ba3]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x346113]
./cpp_example() [0x8048a81]
Leak 3@0xb77e66d0; leaked 4 bytes at position 0x8e791f0
/usr/lib/i386-linux-gnu/libstdc++.so.6(_Znwj+0x27) [0x2f19d7]
./cpp_example(_ZN10my_class_b3fooEv+0x12) [0x8048caa]
./cpp_example(main+0x77) [0x8048baf]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x346113]
./cpp_example() [0x8048a81]
Leak 4@0xb77e66d0; leaked 1 bytes at position 0x8e79250
/usr/lib/i386-linux-gnu/libstdc++.so.6(_Znwj+0x27) [0x2f19d7]
./cpp_example(_Z12cpp_functionv+0x12) [0x8048b26]
./cpp_example(c_function+0xb) [0x8048b36]
./cpp_example(main+0x7c) [0x8048bb4]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x346113]
./cpp_example() [0x8048a81]
fredrik@ubuntu-01:~/Development/leakfinder$
Points of interest
Like I stated at the beginning of the article, this is not an attempt at a finished leak detection product but an explanation of how such a product can be created. The observant reader will have realised that the implementation included with this article is lacking in many areas, it does not explicily cater for (for example) calloc and that using a std::vector to store the allocation_info objects results in a linear performance penalty on the lookups.
Regardless of this, I hope the article has provided a certain amount of insight into how leaks can be spotted in a non-intrusive way (in the sense that the application code does not need to be instrumented or otherwise augmented).
And weirdly enough, the Code Project article submission wizard does not allow me to upload .tar files, so if you want one of those instead; you can get it from here (the allocation_info.cpp file is broken but I'll fix that at a later time, the .zip file is correct); https://sites.google.com/site/fredrikbornander/Home/leakfinder.tar?attredirects=0&d=1
Any comments are most welcome.
相关推荐
有了它,再也不怕 C++ 内存泄露了!
C/C++ 程序越复杂,内存的管理显得越重要,稍有不慎就会出现泄漏。如果内存泄漏不是很严重,在短时间内对程序不会有太大影响,这也使得内存泄漏问题有很强的隐蔽性,不易被发现。 然而不管内存泄漏多么轻微,当程序长时间运行时,其破坏力是惊人的 - 从性能下降到内存耗尽,甚至会影响其他程序的正常运行。
[Unity][ECS]学习笔记(一)
Unity EOS DOTS 学习笔记
C++内存泄露处理方法
在使用C++进行软件开发时,内存管理是一个核心且不可回避的问题。不当的内存管理不仅会导致内存泄露,还可能引发程序的不稳定甚至崩溃。本文将详细探讨C++中内存泄露的成因、诊断方法以及预防和修复策略,以提高开发者在实际工作中对内存管理的掌控能力。内存泄露指的是程序在申请内存后,未能在不再使用它时正确释放,导致内存资源无法被再次有效利用。长时间运行的程序如果存在内存泄露,将会逐渐消耗完系统所有可用内存,影响程序性能,甚至导致程序和系统的崩溃。正确管理内存是C++编程中至关重要的技能。
Unity3D C#之IL2CPP Windows端隐藏任务栏图标并添加至托盘
1 引言 在这篇文章中,我们实现了点击最小化和关闭菜单将程序隐藏到任务栏的功能,但是这篇文章需要额外一个winform程序来处理任务栏的功能,有没有方法可以不需要依赖其他程序也能实现这个需求呢?当然有的,使用Windows系统提供的API就行了。 我们先来看看完全依靠调用Windows提供的API实现的效果。 ...
用C++从零复刻Wordle:手把手教你实现命令行猜词游戏(含完整源码解析)
本文详细介绍了如何使用C++从零开始实现一个命令行版本的Wordle猜词游戏,包含完整的源码解析和核心模块设计。通过游戏逻辑控制器、单词库管理器等模块的构建,开发者可以学习C++实战技巧和性能优化方法,适合提升编程能力和游戏开发兴趣。
MATLAB绘图中文乱码终极解决方案:从方框到清晰显示的全面指南
本文提供了MATLAB绘图中文乱码的全面解决方案。针对中文显示为方框的问题,从单次绘图的临时字体设置,到通过修改默认配置实现一劳永逸,再到系统级字体排查与Java环境配置,给出了详细的步骤指南。文章还涵盖了字体名称指定、图像导出防丢失以及跨平台协作等高级技巧,帮助用户彻底解决中文乱码困扰。
Memory in unity 记录
因此这也是栈分配器的一个限制,它无法快速立刻去重用已经被释放的内存,只有等栈顶被释放的时候才能回去寻找这些连续内存。
UE4载具性能调优指南:从参数设置到实战优化
本文详细解析了UE4载具性能调优的关键技巧,从WheeledVehicle类构建到轮子参数、传动系统设置,再到物理属性与操控调校。通过实战案例分享,帮助开发者优化载具的速度、加速度等核心性能,提升游戏体验。特别适合需要精细调校载具物理特性的开发者参考。
内存不足报错
参考:http://stackoverflow.com/questions/21031491/core-data-info-error-in-console-at-runtimeINFO: fetch-response is unable to open the file /Users/userName/Library/Application Support/iPhone Simulator/7....
Innovus ECO技巧:用setEcoMode+ecoAddRepeater实现批量buffer替换的5个关键步骤
本文详细介绍了在Cadence Innovus工具中,利用setEcoMode与ecoAddRepeater命令实现高效、批量Buffer/Inverter替换的工程化策略。通过启用批处理模式、构建可维护的替换规则、掌握高级定位技巧以及规避布线陷阱等5个关键步骤,帮助芯片设计工程师在ECO阶段大幅提升修改效率与质量,确保设计稳定签核。
[HQChart教程] 去掉“仅学习使用”文字提示的小技巧
最近用 HQChart 做图表时,发现左下角有“仅学习使用”的字样。这种水印虽然是默认的,但如果要展示给客户,可能会显得不够专业。还好,这个问题很好解决,只需要简单修改一下配置。
log4cpp内存泄露
使用中发现log4cpp1.1在使用自己NEW出来的category时正常,但是使用config时就出现内存泄露。 排查发现config创建与NEW创建使用了不一样的接口 void Category::addAppender(Appender& appender) { threading::ScopedLock lock(_appenderSetMutex);
[EdgeAI] NXP eIQ 机器学习Toolkit (一):准备篇
eIQ™机器学习(ML)软件开发环境支持在恩智浦EdgeVerse™微控制器和微处理器(包括i.MX RT跨界MCU和i.MX系列应用处理器)上使用ML算法。eIQ ML软件包括称为eIQ Toolkit的ML工作流工具以及推理引擎、神经网络编译器和优化库。............
C/C++什么是内存泄露,内存泄露如何避免?
C/C++什么是内存泄露,内存泄露如何避免?1. 内存溢出2. 内存泄漏3. 造成内存泄露常见的三种情况3.1 指针重新赋值3.2 错误的内存释放3.3 返回值的不正确处理4. 如何避免内存泄露?5. 内存泄露检测工具valgrind 1. 内存溢出 内存溢出 OOM (out of memory),是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个int,但给它存了long才能存下的数,那就是内存溢出。 2. 内存泄漏 内存泄露 memory lea
基于 Simulink 的**单相全桥 LLC 谐振双向 DC-DC 变换器(带软开关特性)**实战教程
原边(Primary):全桥逆变电路(4个 MOSFET/IGBT),将直流斩波为高频方波。变压器:提供电气隔离和电压变比。副边(Secondary):全波或全桥整流电路(同步整流或二极管)。
多跳问答技术解析:从原理到金融领域实践
多跳问答(Multi-hop QA)是自然语言处理中的高级任务,要求系统通过多步推理整合分散信息来回答问题。其核心技术在于理解文档间的复杂关联,相比单跳问答需要更精细的数据集设计和模型架构。在金融、医疗等专业领域,多跳问答能有效处理如'腾讯大股东投资的港股上市公司'这类需要串联多个信息点的问题。典型实现方案包括MR-Search框架的三级检索器设计,分别处理实体召回、关系匹配和数值比较。当前最佳实践表明,结合稠密检索与稀疏检索的混合策略,配合动态路由机制,能在保持较高精度的同时控制计算成本。这类技术在金融风
CPP-基础:内存泄露及其检测工具
[转]浅谈C/C++内存泄露及其检测工具 对于一个c/c++程序员来说,内存泄漏是一个常见的也是令人头疼的问题。已经有许多技术被研究出来以应对这个问题,比如 Smart Pointer,Garbage Collection等。Smart Pointer技术比较成熟,STL中已经包含支持Smart Pointer的class,但是它的使用似乎并不广泛,而且它也不能解决所有的问题;G...
android 蓝牙通讯demo
android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。
C++内存泄露排查
内存泄漏是指程序动态分配的内存未能及时释放,导致系统内存逐渐耗尽,最终可能造成程序崩溃或性能下降。在C++中,内存泄漏通常发生在使用new或malloc等分配内存的操作时,但没有正确地使用delete或free来释放这块内存。在日常开发过程中,为了避免内存泄露,一般都用智能指针去自动管理内存,避免忘记释放。
Outlook Express 6 (OE6) for Win7/Win8/Win10
英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。
C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片
基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正
log4cpp退出时内存泄露的修复方案
1.缘由 一直对log4cpp非常有好感,就在自己的项目中集成了log4cpp1.1.1版本,并围绕着它建立了一系列的封装函数方便外部调用。写完了一个测试代码后,忽然想看看自己写的程序有没有内存泄露问题。在打开了内存检查参数后发现,有程序退出时有不少内存没有释放。由于我的测试程序很小,所以很快就定位到了原来是log4cpp退出时有Appender对象没有释放。拿起谷歌搜了一把网上没有很好的解决方案...
浅谈C++中的那些内存泄露
虽然学过C语言,但是C++里面的一些基础还是不太懂,还需要再掌握。老范也开始要讲C++设计模式了,必须快点看了,不然就要白花窝滴钱了。 对于内存泄露,我的个人理解就是程序在运行过程中,自己开辟了空间,用完这块空间后却没有释放。今晚上我就犯了这样的低级错误,导致程序没有运行出来,还是先看代码: #include /* run this program using the conso
C++内存泄露和检测
C++中的内存泄露一般指堆中的内存泄露。堆内存是我们手动malloc/realloc/new申请的,程序不会自动回收,需要调用free或delete手动释放,否则就会造成内存泄露。内存泄露其实还应该包括系统资料的泄露,比如socket连接等,使用完后也要释放。 内存泄露的原因: 总结下来,内存泄露大概有一下几个原因: 1、编码错误:malloc、realloc、new申请的内存在堆上,需要手...
什么是内存泄露,如何避免内存泄露 C++
定义 内存泄露(memory lock)是指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。 进一步解释 内存泄露其实并非指内存再物理上的小时,而是在应用程序分配某段内存后,由于涉及错误,死去了对该内存的控制,因而造成了内存的浪费。 一般常说的内存泄露是指堆内存的泄露,堆是动态分配内存的,并且可以分配使用很大的内存,使用不好会产生内存泄露,使用不好会产生内存泄露。频繁的使用mallo...
c++内存泄露之重载new运算符
C++程序的复杂性很大一部分在于他的内存管理,没有C#那样的垃圾回收机制,内存管理对初学者来说很困难。经常会出现内存泄露的情况。那么我们写程序如何避免内存泄露呢?首先我们需要知道程序有没有内存泄露,然后定位到底是哪行代码出现内存泄露了,这样才能将其修复。 本文描述了如何检测内存泄露。最主要的是纯C,C++的程序如何检测内存泄露。 现在有很多专业的检测工具,比如比较有名的BoundsCh
C++ 如何避免内存泄露
转载于:https://zhuanlan.zhihu.com/p/51898119 前言 近年来,讨论 C++ 的人越来越少了,一方面是由于像 Python,Go 等优秀的语言的流行,另一方面,大家也越来越明白一个道理,并不是所有的场景都必须使用 C++ 进行开发。Python 可以应付大部分对性能要求不高的场景,Go 可以应付大部分对并发要求较高的场景,而由于 C++ 的复杂性,只有在对性能...
3936



被折叠的 条评论
为什么被折叠?



