Memory leak

xiaoxiao2021-03-18  222

Talking about Memory Leakage (1) For a C / C programmer, memory leaks is a common and headache. There have been many technologies to be studied to deal with this problem, such as Smart Pointer, Garbage Collection, etc. Smart Pointer technology is relatively mature, and the STL already contains Class of Smart Pointer, but its use does not seem broad, and it does not solve all the problems; Garbage Collection technology is more mature in Java, but in C / C field The development is not smooth, although some people think about the support of GC in C . The real world is like this, as a C / C programmer, memory leak is your feelings in your heart. However, in now there are many tools to help us verify the existence of memory leaks, find out the code that has problems. The definition of memory leaks generally said that the memory leakage is the leakage of the stack memory. The stack memory is that the program is allocated from the heap, the size is arbitrary (the size of the memory block can be determined in the program running period), and the released memory must be displayed after use. Applications generally allocate functions such as Malloc, Realloc, New, from the heap, after use, the program must be responsible for the corresponding call free or delete release the memory block, otherwise, this memory cannot be used again, we will Said this memory leak. The following small program demonstrates the situation in which a pile of memory leaks: void myfunction (int nsize) {char * p = new char [nsize]; if (! GetStringFrom (p, nsize)) {MessageBox ("Error"); Return } ... // Using the string pointed by p; delete p;} An example of a function getStringFrom () When zero is returned, the pointer P pointing to the memory will not be released. This is a common situation in which memory leaks occur. The program allocates memory at the entrance, releases memory at the exit, but the C function can exit anywhere, so once the memory that should be released at an exit, the memory leak will occur. Generalized saying that memory leaks not only contains leaks of stacks, but also contains resource leaks, such as core states, GDI Object, Socket, Interface, etc., fundamentally, these objects allocated by the operating system are also The memory is consumed, and if these objects, the leakage will ultimately lead to the leakage of memory. Moreover, some object consumes core state memory, which will cause the entire operating system unstable when it is severely leaked. Therefore, the leakage of system resources is more serious than the leakage of the stack memory. GDI Object Leak is a common resource leak: void cmyview :: onpaint (cdc * pdc) {cbitmap bmp; cbitmap * PoldBMP; bmp.loadbitmap; PoldBMP = PDC-> SelectObject (& BMP); ... IF Something ()) {Return;} PDC-> SelectObject (PoldBMP); Return;} Example Something () When returning non-zero, the program does not select PoldBMP back to PDC before exiting, which will cause PoldBMP to point to Hbitmap object leaks. If this program is run for a long time, it may cause the entire system.

This problem is easier to expose under Win9X because Win9x's GDI stack is much smaller than Win2K or NT. Memory leakage happens: Classification in a way, memory leaks can be divided into 4 categories: 1. Avantime memory leakage. The code that occurs in memory leaks will be executed multiple times, and a memory leak will be caused each time it is executed. For example, if the Something () function has returned True, the HbitMap object points to PoldBMP always leaks. 2. Appetitive memory leak. The code that occurs in memory leaks only occurs under certain specific environments or operations. For example, if the Something () function returns TRUE in a particular environment, the HBitMap object pointing to PoldBMP does not always leak. Avantages and occasionalities are relative. For a specific environment, the suspended may become a normal. So the test environment and test methods are critical to detect memory leaks. 3. Disposable memory leakage. The code that has a memory leak will only be executed once, or due to the defects on the algorithm, there will always be only a piece of only and one memory leak. For example, allocate memory in the constructor of the class, but does not release the memory in the destructuring function, but because this class is a Singleton, the memory leak will only occur once. Another example: char * g_lpszFileName = NULL; void SetFileName (const char * lpcszFileName) {if (g_lpszFileName) {free (g_lpszFileName);} g_lpszFileName = strdup (lpcszFileName);} if the program according to a third g_lpszFileName not released at the end point String, then, even if setFileName () multiple times, there will always be a memory, and only one memory is leaking. 4. Implicit memory leaks. The program is constantly allocated during the running process, but the memory is released until the end. Strictly speaking, there is no memory leak, because the final program releases all application memory. But for a server program, it is necessary to run a few days, several weeks or even months, and the memory may not be released in time may result in all memory of the final depletion system. Therefore, we call such memory leaks for implicit memory leaks.

To give an example: Class Connection {Public: Connection (Socket S); ~ connection (); ... private: socket _socket; ...}; class connectionManager {} ~ connectionManager () {} ~ connectionManager () {} ~ connectionManager () {list :: ipnectionManager () { For (IT = _Connlist.begin (); it! = _Connlist.end (); IT) {delete (* it);} _connlist.clear ();} void onclientconnected (socket s) {connection * p = new Connection (s); _Connlist.push_back (p);} void OnClientDisconnected (Connection * PConn) {_Connlist.Remove (PCONN); delete PCONN;} private: list _connlist;}; example four leave in Client from Server end Server does not call the onClientDisconnected () function, then the Connection object that represents the connection will not be deleted in time (when the server program exits, all Connection objects are deleted in the destructor of the ConnectionManager). When there is a continuous connection, it happens when it is disconnected. From the perspective of the user using the program, the memory leak itself does not happen, as a general user, it does not feel the existence of memory leaks. Really harmful is the accumulation of memory leaks, which will eventually exhaust all the memory of the system. From this perspective, there is no harm of disposable memory leaks because it does not accumulate, and the harmful memory leakage is very large, because it is more difficult to detect more than a haired and occasive memory. . Talking about Memory Leak (2) Testing Instruction Leakage: The key to detect memory leaks is to capture the call to allocating memory and release memory. Intercept these two functions, we can track the life cycle of each memory, for example, after the successful assignment of memory, add its pointer to a global list; whenever you release a memory, then put it The pointer is removed from the list. Thus, when the program ends, the remaining pointers in the List are pointing to those that are not released. Here is just a simple description of the basic principle of detecting memory leaks, detailed algorithms can see Steve MAGUIRE <

". If you want to detect the leak of the stack, you can intercept Malloc / Realloc / Free and New / Delete can be used (in fact new / delete ultimately using Malloc / Free, so as long as you intercepting a group). For other leaks, a similar approach can be used to capture the corresponding allocation and release functions. For example, to detect the leak of BSTR, you need to intercept Sysallocstring / Sysfreestring; to detect HMENU leaks, you need to intercept CreateMenu / DestroyMenu. (Some resource allocation functions have multiple, the release function is only one, for example, SysallocStringlen can also be used to allocate BSTR. At this time, multiple allocation functions need to be intercepted under the Windows platform, and the tools that detect memory leaks are commonly used. Introduction, MS C-Runtime Library built-in detection function; external detection tool, such as Purify, BoundSchecker, etc .; Performance Monitor with Windows NT. The three tools have advantages and disadvantages, although the MS C-Runtime Library is functionally weak, but it is free; Performance Monitor does not indicate the code of the problem, but it can detect implicit The existence of memory leaks, this is where two other types of tools are unable to force. The following we will discuss these three test tools: the detection method of memory leakage under the VC uses the application of MFC development, and after compiling in Debug mode, the memory leak detection code will be automatically added. After the program is over, if a memory leak has occurred, all the information of all leaks occurred in the debug window, the following two lines show information of the leak-leaked memory block: E: /TESTMEMLEAK/testdlg.cpp 70): {59} Normal Block AT 0x00881710, 200 BYTES Long. Data:

61 62 63 64 65 66 67 6D 6D 6E 6E 6F 70 The first line shows that the memory block is assigned by the testdlg.cpp file, the 70r-line code code, the address is 0x00881710, the size is 200 bytes, {59} means Call the REQUEST ORDER of the memory allocation function, and more information about it can see the help of _CRTSetBreakalloc () in MSDN. The second line shows the contents of the 16 bytes of the memory block, and the angle brackets are displayed in an ASCII mode, followed by a 16-way method. Generally, everyone is mistaken to think that the detection function of these memory leaks is provided by the MFC, which is not. The MFC is only packaged and utilized with the Debug Function of MS C-Runtime Library. Non-MFC programs can also use the Debug Function of MS C-Runtime Library to join memory leak detection. MS C-Runtime Library has built a memory leak detection function when implementing functions such as Malloc / Free, StrDUP. Note Observing the project generated by the MFC Application Wizard, there is such a macro definition at the head of each CPP file: #ifdef _debug #define new debug_new #undef this_file static char this_file [] = __file__; #ndif has this Definition, when compiling the Debug version, all NEWs appear in this CPP file are replaced into debug_new. So what is debug_new? Debug_new is also a macro. The following is taken from AFX.H, 1632 row #define debug_new new (__), if there is such a line of code: char * p = new char [200]; through the macro replacement becomes: char * p = New (this_file, __line __) char [200]; according to C standards, for the above new use method, the compiler will find this defined Operator New: void * Operator New (size_t, lpcstr, int) We are in Afxmem .cpp 63 found one such line operator new to achieve void * AFX_CDECL operator new (size_t nSize, LPCSTR lpszFileName, int nLine) {return: perator new (nSize, _NORMAL_BLOCK, lpszFileName, nLine);} void * __cdecl operator new ( size_t nSize, int nType, LPCSTR lpszFileName, int nLine) {... pResult = _malloc_dbg (nSize, nType, lpszFileName, nLine); if (pResult = NULL) return pResult;! ...} The second operator new function is relatively long, for simplicity During the period, I only extracted some. Obviously the last memory distribution is also implemented through the _malloc_dbg function, this function belongs to the debug function of MS C-Runtime Library. This function not only requires the size of the memory, but also two parameters of the file name and the line number. The file name and the line number are used to record which code caused by this assignment. If there is no release before the program ends, then this information will be output to the Debug window. Here, this_file, __ file and __line__ are encompasced here.

__File__ and __line__ are all macros defined by the compiler. When it comes to __file__, the compiler will replace __file__ to replace it with a string, which is the path name of the current compiled file. When it comes to __line__, the compiler will replace __line__ to a number, this number is the line number of the current line code. In the definition of debug_new, it is not directly used in __file__, but used this_file, the purpose is to reduce the size of the target file. Assuming that there is 100 in a CPP file, if you use __file__ directly, the compiler generates 100 constant strings, which is the path name of the 飧? / Span> CPP file, clear Very redundant. If you use this_file, the compiler will only generate a constant string, and the 10 calls used by New are pointers that point to the constant string. Ob again observing the project generated by the MFC Application Wizard, we will find that only the NEW is only mapped in the CPP file. If you use the malloc function directly in the program, call Malloc's file name and the line number will not be recorded. of. If this memory has leaks, MS C-Runtime Library can still detect, but when the information of this memory block is output, the file name and line number allocated there is no. To open memory leak in non-MFC programs is very easy, you only need to add the following lines of code at the entrance of the program: int TMPFLAG = _CRTSETDBGFLAG (_CRTDBG_REPORT_FLAG); TMPFLAG | = _CRTDBG_LEAK_CHECK_DF; _CRTSETDBGFLAG (TMPFLAG); this, in the program At the end, it is the Winmain, Main or Dllmain function returns, if there is still memory blocks, their information is printed into the Debug window. If you try to create a non-MFC application, add the above code at the entry of the program, and deliberately do not release some memory blocks in the program, you will see the following information in the debug window: {47} Normal Block AT 0x00C91C90, 200 BYTES Long. Data: <> 00 01 02 03 04 05 06 07 08 09 0B 0B 0D 0E 0F memory leak detected, but the file name and row number. For a relatively large program, there is no such information, and the problem will become very difficult. In order to be able to know where the leak memory block is allocated, you need to implement a mapping function similar to the MFC, map New, MAOLLOC and other functions to the _malloc_dbg function. Here I am not described again, you can refer to the source code of the MFC. Since the debug function is implemented in the MS C-Runtimelibrary, it can only detect the leaks of the heap memory and is limited to the memory allocated by Malloc, Realloc or StrDUP, and those system resources, such as Handle, GDI Object, or not pass The memory allocated by C-Runtime Library, such as the leak of Variant, BSTR, which is impossible to detect, this is a major limitations of this test method. In addition, in order to record where the memory block is allocated, the source code must cooperate, which is very troublesome to debug some old procedures. After all, the source code is not a worries, this is another test method. A limiter.

For developing a large program, the detection function provided by MS C-Runtime Library is far less than enough. Let's take a look at the external test tool. What is much more is BoundSchecker, one because it is more comprehensive, more important is its stability. Such tools are not stable, but will be busy with it. In the end, it is from the famous Numega, I have basically no big problem. Talking about Memory Leak (3) Use BoundSchecker to detect memory leaks: BoundSchecker uses a technology called Code Injection to intercept the call to allocate memory and release memory. Simply put, when your program starts running, BoundSchecker's DLL is automatically loaded into the address space (this can be implemented via System-Level), then it will modify the function call to memory allocation and release in the process. Let these calls first transfer to its code, then perform the original code. When BoundSchecker is doing these actions, there is no need to modify the source code or engineering profile of the debugged program, which makes it easy and straightforward. Here we take the malloc function as an example, intercepting other function methods is similar. The function that needs to be intercepted may be in the DLL, or in the code of the program. For example, if the C-Runtime Library is static, then the code of the Malloc function will be connected to the program. In order to seize the call to such a function, BoundSchecker dynamically modifies the instructions of these functions. The following two assembly code, BoundsChecker no intervening period, there is involved another section of BoundsChecker: 126: _CRTIMP void * __cdecl malloc (127: size_t nSize 128:) 129: {00403C10 push ebp 00403C11 mov ebp, esp 130: return _nh_malloc_dbg ( nSize, _newmode, _NORMAL_BLOCK, NULL, 0); 00403C13 push 0 00403C15 push 0 00403C17 push 1 00403C19 mov eax, [__ newmode (0042376c)] 00403C1E push eax 00403C1F mov ecx, dword ptr [nSize] 00403C22 push ecx 00403C23 call _nh_malloc_dbg (00403c80 ) 00403C28 add esp, 14h 131:} the following this section of code is BoundsChecker involved: 126: _CRTIMP void * __cdecl malloc (127: size_t nSize 128:) 129: {00403C10 jmp 01F41EC8 00403C15 push 0 00403C17 push 1 00403C19 mov eax, [__ newmode (0042376c)] 00403C1E push eax 00403C1F mov ecx, dword ptr [nSize] 00403C22 push ecx 00403C23 call 00403C28 add esp, 14h 131 _nh_malloc_dbg (00403c80):} when BoundsChecker intervention, function malloc front three assembler instruction is replaced by a jmp Instructions, the original three instructions are moved to address 01f41ec8. When the program enters Malloc, first JMP to 01F41EC8, execute the original three instructions, and then the BoundSchecker's world.

The return address of the function is roughly recorded (the return address of the function is on the stack, so it is easy to modify), then point the return address to the code belonging to the Boundschecker, then jumps to the original directive of the malloc function, that is, at 00403C15 . When the Malloc function is over, since the return address is modified, it will return to the BoundSchecker code, and BoundSchecker records the pointer to the memory allocated by MalloC, and then jumps to the original return address. If the Memory Allocation / Release Function is in the DLL, BoundSchecker uses another method to intercept the call to these functions. Boundschecker lets the function address in Table points to your address by modifying the function address in Table to the purpose of the interception. Regarding how to intercept Windows system function, "Programmer" Magazine 2002, "API Hook revealed (below)," a summary of the modified import address table. I will not go to it again. Intercept these allocation and release functions, BoundSchecker can record the life cycle of the assigned memory or resource. The next question is how the source code is related, that is, when BoundSchecker detects memory leaks, how do it report which code assignment of this memory block. The answer is debug information. When we compile a Debug version, the compiler records the corresponding relationship between the source code and the binary code, put it in a separate file (.pdb) or directly connect into the target Cheng Xiao S Xin Sui ┬ 畔   魇 芡 啥 系 系 柚  ゲ ゲ 校 校 校 校 校 格 格 格 格 δ 格 格 格 格 格 格 格 格 格 多 多 格 格 格 格 格 格 格 多 格 格 格 格 格 格 格Which file is on the source code of a block, which is on. Using Code Injection and Debug Information so that BoundSchecker can not only record the source code of the call assignment function, but also record the source code location of the Call Stack when allocated, and the source code location of the function on the Call Stack. This is very useful when using class libraries like MFC, here I use an example: void showxItemMenu () {... cmenu menu; menu.createpopupnu (); // add menu items. Menu.trackpropupnupmenu (); ...} Void showyitemmenu () {... cmenu menu; menu.createpopupmenu (); // add menu items. menu.trackpropupnupMenu (); menu.detach (); // this will cause hmenu leak ...} Bool CMenu :: createPopUpMenu () { . However, for BoundSchecker, the leak HMENU is assigned in Class CMenu :: CreatePopUpMenu (). Suppose your program has many places using cmenu's createPopupMenu () function, such as   皇 嫠吣 嫠吣 孤 ┦ ┦ ┦?? 怯 怯 怯 怯 怯 怯 怯 怯 ┦ ┦ ┦ ┦ ┦  的   的 的 的 的Where, in ShowXItemMenu () or in ShowyItemMenu (), or other places also use createPopupUpMenu ()? With the information of Call Stack, the problem is easy.

BoundSchecker reports the following reports of the HMENU information: Function File Line CMenu :: CreatePopUpMenu E: 68 / VC98 / MFC / MFC / include / AFXWIN1.INL 1009 ShowyItemMenu E: /TESTMEMLEAK /MYTEST.CPP 100 省 其他 省 省 省 省 省 省In this way, we can easily find a function of a problem that ShowyItemMenu (). When programming class libraries such as MFC, most of the API calls are encapsulated in the class library, with the Call Stack information, we can very easy tracking to truly leak. Recording the Call Stack information makes the running of the program very slow, so BoundSchecker does not record Call Stack information by default. You can open the option switch of the recorded Call Stack information as follows: 1. Open the menu: Boundschecker | Setting ... 2. In the ERROR Detection page, select Custom 3 in the List of Error Detection Scheme. Select Pointer and in Category. Leak Error Check 4. Hook on the Report Call Stack checkbox 5. Click OK Based on Code Injection, BoundSchecker also provides the verification function of the API Parameter, Memory Over Run. These features are very beneficial for the development of procedures. Since these contents do not belong to this theme of this article, it is not detailed here. Although BoundSchecker is so powerful, it is still pale with implicit memory leaks. So let's take a look at how to detect memory leaks with Performance Monitor. Using Performance Monitor to detect the kernel of memory leaks NT in the design process has already joined system monitoring functions, such as CPU usage, memory usage, usage of I / O operations as one counter, application can be read Take these Counters to understand the health of the entire system or a process. Performance Monitor is such an application. In order to detect memory leaks, we can generally monitor the Handle Count, Virutal Bytes, and Working Set of the Process object. The Handle Count records the number of Handle, which is currently open. Monitor this Counter helps us find whether the program has a Handle leak; Virtual Bytes records the size of the virtual memory used in the virtual address space, NT memory allocation The method of two steps, first, retain a space on the virtual address space, when the operating system does not assign physical memory, just retains a piece of address. Then, then submit this space, then the operating system is allocated physical memory. So, Virtual Bytes is generally greater than the program's Working SET. Monitoring Virutal Bytes can help us find some problems under the system; Working Set records the total amount of memory that the operating system has submitted, this value and the total amount of memory applications have a close relationship. If the program has a memory leak, this The value will continue to increase, but Virtual Bytes is an increase in jumping. Monitoring these Counters allow us to understand the situation of the process using memory, if a leak occurs, even implicit memory leaks, these Counters will continue to increase.

转载请注明原文地址:https://www.9cbs.com/read-129945.html

New Post(0)