内联字符串中的内存泄漏 [英] Memory Leaks on inline string

查看:83
本文介绍了内联字符串中的内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Windows 7专业版SP1下的Visual Studio 2017社区157.5。


我的应用程序是MFC应用程序,MFCPropertySheet作为主窗口。


当我在头文件中时

 

// Layer.h

template< int idx>
struct layer_traits
{};

emplate<>
struct layer_traits< 0>
{
......................................... .....;

static const string_t m_layer_name;

};


使用LT0 = layer_traits< 0> ;;

和cpp文件

 

// Layer.cpp


// LT0
const string_t LT0 :: m_layer_name =

string_t(_T(" Input"));

一切正常;


但如果我做这个字符串内联成员,如

 

// Layer.h
{

......... ...............................................
inline static const string_t m_layer_name = string_t(_T(" Input"));

......................... .......................................

};

并从Layer.cpp中删除定义,一切正常,直到我关闭应用程序。


点击后OK按钮应用程序关闭,如

检测到内存泄漏! 
转储对象 - >
{8362}正常块,位于0x00774EB8,长度为8个字节。
数据:< 0> 14 D2 30 00 00 00 00 00
{8361}正常块位于0x00774E80,长度为8个字节。
数据:< 0> 14 D2 30 00 00 00 00 00
{8360}正常块位于0x00774E48,长度为8个字节。
数据:< 0> 14 D2 30 00 00 00 00 00
{8359}正常块位于0x00774E10,长度为8个字节。
数据:< 0> 14 D2 30 00 00 00 00 00
{283}正常阻塞位于0x008A4F28,8个字节长。
数据:< 0> 14 D2 30 00 00 00 00 00
{282}正常块位于0x008A4EF0,长度为8个字节。
数据:< 0> 14 D2 30 00 00 00 00 00
{267}正常阻塞位于0x008A4BE0,长度为8个字节。
数据:< 0> 14 D2 30 00 00 00 00 00
{230}正常块位于0x00894820,长度为8个字节。
数据:< 0> 14 D2 30 00 00 00 00 00
{229}正常块位于0x008947E8,长度为8个字节。
数据:< 0> 14 D2 30 00 00 00 00 00
{227}正常块位于0x00894778,长度为8个字节。
数据:< 0> 14 D2 30 00 00 00 00 00
对象转储完成。
程序'[7788] ANNet.exe'退出时代码为0(0x0)。

如果我只是停止调试通过 调试菜单,没有报告内存泄漏。


这个sruct,LT0,iis用于多个函数。



我想使用内联定义并删除cpp文件,但是如何消除这些泄漏?


任何帮助?







$



解决方案

问题是,字符串"输入"是12个字节长,如果它来自它将不会有你所看到的那种值。 


当然,一种简单的方法来测试,看看是否这是因为字符串是从类中删除字符串本身并查看是否仍然发生内存泄漏。


如果你不能这样做,有一点要做的就是简单比如改变字符串的值。如果这是由字符串类引起的,那么这些块的内容自然会改变。


但这看起来像是用new或malloc分配一个8字节的整数,然后它正在被初始化为值0x30d214,或十进制的3,119,508。基本上,你正在泄漏内存,但字符串是你做的最后一次更改。


你可以尝试做的一件事是在你的应用程序中启用调试堆。这样做会改变您看到的输出:


检测到内存泄漏!

转储对象 - >

c:\ users\darran\source\repos\mehagain\mehagain\main.cpp(13):{74}正常阻止位于0x000002037F1DDA30,8个字节长。

 数据:< ;&NBSP; 0&NBSP;&NBSP;&NBSP;&NBSP; > 14 D2 30 00 00 00 00 00
$
c:\ usersrs \ darran\source \repos\mehagain\mehagain\main.cpp(10):{73}正常阻止0x000002037F1DDB20,8个字节长。

 数据:<  0&NBSP;&NBSP;&NBSP;&NBSP; > 14 D2 30 00 00 00 00 00
$
对象转储完成。


注意输出中的文件名和行号是多少?这是分配内存的源文件的文件名和行号。第一个(底部的)是使用malloc分配的,其中第二个(前一个)使用new分配


要启用此功能,需要调试配置,在包含任何头文件之前,定义_CRTDBG_MAP_ALLOC。您也可以将其作为命令行中的定义。

 #define _CRTDBG_MAP_ALLOC 
#include< new>
#include< crtdbg.h>

int main()
{
long long * a = static_cast< long long *>(malloc(sizeof(long long)));
* a = 0x30d214;

_CrtDumpMemoryLeaks();
返回0;
}

这样做会使malloc成为一个宏,并且在调试配置中它会用以下内容替换malloc:


< pre class ="prettyprint"> #define _CRTDBG_MAP_ALLOC
#include< new>
#include< crtdbg.h>

int main()
{
long long * a = static_cast< long long *>(_ malloc_dbg(sizeof(long long),_ NORMAL_BLOCK,__ FILE __,_ _ _ _ __));
* a = 0x30d214;

_CrtDumpMemoryLeaks();
返回0;
}

这为调试堆提供源文件和行信息,因此它可以报告这些内存块的分配位置,以帮助您进行调试。 _NORMAL_BLOCK只是crtdbg.h中定义的值,而__FILE__和__LINE__只是预定义的预处理器
宏。


你可以用new做类似的东西,并且有它使用新的特殊展示位置版本来记录此信息。在new中,有一个运算符new()的版本,其中包含以下参数:

 void * __CRTDECL operator new(
_In_ size_t _Size,
_In_ int _BlockUse,
_In_z_ char const * _FileName,
_In_ int _LineNumber
);

这些看起来很熟悉吧?无论如何,如果你使用new并提供这些参数,那么你可以让调试堆来记录所有这些信息。

 #define _CRTDBG_MAP_ALLOC 
#include <新>
#include< crtdbg.h>

int main()
{
long long * b = new(_NORMAL_BLOCK,__ FILE __,_ _ ____)long long;
* b = 0x30d214;

_CrtDumpMemoryLeaks();
返回0;
}

但是将所有这些内容写成新内容是一件痛苦的事情,因此有可能将其变为宏:

 #define _CRTDBG_MAP_ALLOC 
#include< new>
#include< crtdbg.h>

#define DEBUG_NEW new(_NORMAL_BLOCK,__ FILE __,_ _ 11__)
#define new DEBUG_NEW

int main()
{
long long * b =新长; //用DEBUG_NEW宏替换new
* b = 0x30d214;

_CrtDumpMemoryLeaks();
返回0;
}

所以你需要做的就是写新的。


显然这会妨碍任何安置你可能写的新闻。在这种情况下,您应该使用#undef来取消定义new(可能将其包装在#pragma push_macro("new")和#pragma pop_macro("new")中以使事情变得更容易)。


这些应该使用new或malloc将文件名和行号添加到代码分配的任何内存中。


It is Visual Studio 2017 Community 157.5 under Windows 7 Pro SP1.

My app is MFC app with MFCPropertySheet as a main window.

When I have in the header file

// Layer.h template <int idx> struct layer_traits {}; emplate<> struct layer_traits<0> { ..............................................;

static const string_t m_layer_name; }; using LT0 = layer_traits<0>;

and in the cpp file

// Layer.cpp // LT0 const string_t LT0::m_layer_name =

string_t(_T("Input"));

all works just fine;

But if I make this string an inline member like

// Layer.h {

........................................................ inline static const string_t m_layer_name = string_t(_T("Input"));

................................................................

};

and remove definitions from Layer.cpp, all also works just fine, until I close the app.

After I click OK button the app closes with mellages like

Detected memory leaks!
Dumping objects ->
{8362} normal block at 0x00774EB8, 8 bytes long.
 Data: <  0     > 14 D2 30 00 00 00 00 00 
{8361} normal block at 0x00774E80, 8 bytes long.
 Data: <  0     > 14 D2 30 00 00 00 00 00 
{8360} normal block at 0x00774E48, 8 bytes long.
 Data: <  0     > 14 D2 30 00 00 00 00 00 
{8359} normal block at 0x00774E10, 8 bytes long.
 Data: <  0     > 14 D2 30 00 00 00 00 00 
{283} normal block at 0x008A4F28, 8 bytes long.
 Data: <  0     > 14 D2 30 00 00 00 00 00 
{282} normal block at 0x008A4EF0, 8 bytes long.
 Data: <  0     > 14 D2 30 00 00 00 00 00 
{267} normal block at 0x008A4BE0, 8 bytes long.
 Data: <  0     > 14 D2 30 00 00 00 00 00 
{230} normal block at 0x00894820, 8 bytes long.
 Data: <  0     > 14 D2 30 00 00 00 00 00 
{229} normal block at 0x008947E8, 8 bytes long.
 Data: <  0     > 14 D2 30 00 00 00 00 00 
{227} normal block at 0x00894778, 8 bytes long.
 Data: <  0     > 14 D2 30 00 00 00 00 00 
Object dump complete.
The program '[7788] ANNet.exe' has exited with code 0 (0x0).

If I just stop debugging via  Debug menu, no memory leaks are reported.

This sruct, LT0, iis used in several functions.

I would like to use inline definitions and get rid of cpp file, but how to kill these leaks?

Any help?

.

解决方案

The issue is, the string "Input" is 12 bytes long, and if it was coming from it wouldn't have the kinds of values that you are seeing. 

Of course, one easy way to test out to see if this is caused by the string is to remove the string itself from the class and see if the memory leaks still occur.

If you can't do that, one thing to try is a simple thing like changing the value of the string. If this was caused by the string class then the contents of these blocks will naturally change.

But this looks like you are allocating, either with new or malloc, an 8 byte integer, and it is being initialised to the value 0x30d214, or 3,119,508 in decimal. Basically, you are leaking the memory, but the string was the last change you done.

One thing you could try to do is enable the debug heap in your application. Doing so will change the output you are seeing to:

Detected memory leaks!
Dumping objects ->
c:\users\darran\source\repos\mehagain\mehagain\main.cpp(13) : {74} normal block at 0x000002037F1DDA30, 8 bytes long.
 Data: <  0     > 14 D2 30 00 00 00 00 00
c:\users\darran\source\repos\mehagain\mehagain\main.cpp(10) : {73} normal block at 0x000002037F1DDB20, 8 bytes long.
 Data: <  0     > 14 D2 30 00 00 00 00 00
Object dump complete.

notice how there is the file name and line number in the output? This is the file name and line number of the source file where the memory was allocated. The first one (the bottom one) was allocated using malloc, where the second one (the top one) was allocated using new.

To enable this requires the debug configuration, and prior to including any of the header files, define _CRTDBG_MAP_ALLOC. You could also put this as a define in the command line.

#define _CRTDBG_MAP_ALLOC
#include <new>
#include <crtdbg.h>

int main()
{
	long long *a = static_cast<long long *>(malloc(sizeof(long long)));
	*a = 0x30d214;
		
	_CrtDumpMemoryLeaks();
	return 0;
}

What this does is this makes malloc into a macro, and in the debug configuration it will replace malloc with:

#define _CRTDBG_MAP_ALLOC
#include <new>
#include <crtdbg.h>

int main()
{
	long long *a = static_cast<long long *>(_malloc_dbg(sizeof(long long), _NORMAL_BLOCK, __FILE__, __LINE__));
	*a = 0x30d214;
	
	_CrtDumpMemoryLeaks();
	return 0;
}

this provides the source file and line information to the debug heap, so it can report where these blocks of memory was allocated to help you debug. _NORMAL_BLOCK is just a value defined in crtdbg.h, and __FILE__ and __LINE__ are just predefined pre-processor macros.

You are able to do something similar with new, and have it use a special placement version of new which records this information. In new, there is a version of operator new() that has the parameters:

void* __CRTDECL operator new(
        _In_   size_t      _Size,
        _In_   int         _BlockUse,
        _In_z_ char const* _FileName,
        _In_   int         _LineNumber
        );

these look familiar right? Anyway, if you use new and provide these parameters, then you can get the debug heap to record all of this information.

#define _CRTDBG_MAP_ALLOC
#include <new>
#include <crtdbg.h>

int main()
{	
	long long *b = new(_NORMAL_BLOCK, __FILE__, __LINE__) long long;
	*b = 0x30d214;

	_CrtDumpMemoryLeaks();
	return 0;
}

But writing all of that stuff for new is a pain, so it is possible to make this into a macro:

#define _CRTDBG_MAP_ALLOC
#include <new>
#include <crtdbg.h>

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

int main()
{	
	long long *b = new long long; //replaces new with the DEBUG_NEW macro
	*b = 0x30d214;

	_CrtDumpMemoryLeaks();
	return 0;
}

so all you need to do is write new.

Obviously this will get in the way of any placement news you may have written. In this case you should just use #undef to undefine new (maybe wrap it in #pragma push_macro("new") and #pragma pop_macro("new") to make things easier).

These should add the file name and line number to any memory allocated by your code either using new or malloc.


这篇关于内联字符串中的内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆