我最近遇到了内存泄漏问题。我在很长一段时间内解决了这个问题,随后发现抛出异常(我使用自己的异常类)会导致内存泄漏。抛出异常的代码如下:
HINSTANCE lib = LoadLibrary(path.c_str());
if(!lib)
{
DWORD werror = GetLastError();
ostringstream stream;
stream << werror;
string errstring = "Error " + stream.str();
errstring.append(" - " + libraryName);
throw LibraryLoadException(MOD_ERROR_LIB_LOAD, errstring.c_str());
}
结果输出如下:
Detected memory leaks!
Dumping objects ->
{351} normal block at 0x0044D208, 32 bytes long.
Data: <Error 126 - note> 45 72 72 6F 72 20 31 32 36 20 2D 20 6E 6F 74 65
{347} normal block at 0x0043BD98, 8 bytes long.
Data: <4 > > 34 F2 3E 00 00 00 00 00
{344} normal block at 0x0043FDE8, 32 bytes long.
Data: <126 > 31 32 36 CD CD CD CD CD CD CD CD CD CD CD CD CD
{302} normal block at 0x004409D8, 8 bytes long.
Data: <4 > > 34 F3 3E 00 00 00 00 00
{301} normal block at 0x0043FAF0, 8 bytes long.
Data: <P > > 50 F3 3E 00 00 00 00 00
Object dump complete.
如在visual studio leak CrtDbg的输出中所见,if块中使用的对象的实际值。所有这些对象,包括异常本身(及其所有属性)都是在堆栈上分配的,所以我不会忘记忘记在堆上释放某些东西。 我凭经验对此进行了测试,泄漏肯定是由if块中的对象引起的(在删除了几个对象之后,如字符串,DWORD和流,泄漏变得更少)。
有谁能告诉我我在做什么(或者什么)错了?
提前谢谢
答案 0 :(得分:0)
至于要求更详细代码的注释,以下是导致内存泄漏的方法:
void ModuleLoader::load(std::string libraryName, std::string type, void(CALLBACK * receiveData)(Message))
{
path = s2ws(libraryName); // conversion to wide string
HINSTANCE lib = LoadLibrary(path.c_str());
if(!lib)
{
DWORD werror = GetLastError();
ostringstream stream;
stream << werror;
string errstring = "Error " + stream.str();
errstring.append(" - " + libraryName);
throw LibraryLoadException(MOD_ERROR_LIB_LOAD, errstring.c_str());
}
DllModule *module = new DllModule(libraryName, lib);
module->setModType(type);
try
{
startModule(module, receiveData);
moduleMap.insert(std::pair<std::string, DllModule *>(type, module));
}
catch (ProbeCoreException e)
{
delete module;
throw e;
}
}
这是一个加载动态模块的单例类的方法,定义如下:
class ModuleLoader
{
// Function pointer definitions
typedef void (*StopFuncPointer)();
typedef int (*StartFuncPointer)(void(CALLBACK * receiveData)(Message));
typedef void (*SetDataFunctionPointer)(Message);
private:
std::map<std::string, DllModule *> moduleMap; // map of loaded modules
std::wstring path;
std::wstring s2ws(const std::string &s);
void startModule(DllModule * module, void(CALLBACK * receiveData)(Message));
void stopModule(DllModule * module);
// singleton functions
ModuleLoader() {}; // private constructor
ModuleLoader(ModuleLoader const&);
void operator = (ModuleLoader const&);
public:
void load(std::string libraryName, std::string type, void(CALLBACK * receiveData)(Message));
void unload(std::string libraryType);
void unloadAll();
vector<DllModule> getLoadedModules();
int containsModuleType(string modType);
HINSTANCE getModuleLibraryByType(std::string type);
// singleton getInstance function
static ModuleLoader & getInstance()
{
static ModuleLoader instance;
return instance;
}
};
s2ws方法将公共字符串转换为宽字符串(我发布它以防万一):
std::wstring ModuleLoader::s2ws(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
我多次检查并在应用程序的所有级别执行异常抛出时释放堆对象。 此外,如果我删除了DWORD,ostringstream和字符串对象(在堆栈上分配),内存泄漏会增加...所以它也必须与这些相关联。我无法想象如何删除这部分代码应该有助于其他地方的堆内存释放:
DWORD werror = GetLastError();
ostringstream stream;
stream << werror;
string errstring = "Error " + stream.str();
errstring.append(" - " + libraryName);
答案 1 :(得分:0)
好的,我设法将泄漏减少到原来的两个中只有两个:
Dumping objects ->
{312} normal block at 0x0045FDC8, 8 bytes long.
Data: <( ( > 28 ED 28 00 00 00 00 00
{311} normal block at 0x0045F810, 8 bytes long.
Data: <D ( > 44 ED 28 00 00 00 00 00
Object dump complete.
我使用了_CrtSetBreakAlloc(x)函数,其中x是泄漏的数量(例如在上面的情况下为311或312)并且找到了,其中分配了未分配的内存。很难相信它,但分配确实发生在这些方面:
string errstring = "Error " + stream.str();
and
errstring.append(" - " + libraryName);
我通过在堆上动态分配字符串和流来删除泄漏,然后创建异常并将其存储在临时变量中,随后解除分配字符串和流变量,最后抛出异常本身:
DWORD werror = GetLastError();
ostringstream *stream = new ostringstream();
*stream << werror;
string *errstring = new string("Error ");
errstring->append(stream->str());
errstring->append(" - ");
errstring->append(libraryName);
ProbeCoreException e = LibraryLoadException(MOD_ERROR_LIB_LOAD,
errstring->c_str());
delete errstring;
delete stream;
throw e;
在将字符串参数传递给“加载”函数本身时,最后两次分配(再次令人难以置信):
loader.load("notexisting.dll", "TEST", &callbackFunction);
我正在考虑由于类是单例而发生的泄漏,该类是根据防漏单例规则创建的,但是,这里提到:
似乎唯一可能摆脱隐藏泄漏的机会是将字符串指针作为参数传递,然后明确地将它们解除错误...