在我的应用程序中(使用MySQL Connector / C ++在Windows 8上开发)我正在创建预准备语句并仅在应用程序结束时删除它们。但是在应用程序运行时,我执行查询并仅删除结果集。
但是,我观察到大量的内存仍然分配,我觉得它超出预期。我使用Visual Leak Detector进行检查,令我惊讶的是,我发现在结果集指针中显示了泄漏,尽管我正在适当地删除它们。
所以我编写的演示程序就是这样做的。那就是创建预备语句,创建查询,获取结果,删除结果(但不要在结束时删除准备好的语句以便我们可以看到泄漏)并退出。这是演示代码MySQL.cpp
:
#include "stdafx.h"
#include <conio.h>
#define CPPCONN_LIB_BUILD // We must define this as we are linking mysql connector in static library. It directs build_config.h to not to put __declspec(dllimport) before function declarations.
#include <driver/mysql_connection.h>
#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>
#include <vld.h> // Visual memory leak detector
int _tmain(int argc, _TCHAR* argv[])
{
sql::Connection *pConnection = NULL;
sql::ResultSet *pResultSet = NULL;
sql::PreparedStatement *pPreparedStatement = NULL;
sql::Driver *driver = NULL;
/* Create a connection */
driver = get_driver_instance();
pConnection = driver->connect("tcp://127.0.0.1:3306", "username", "password");
pConnection->setSchema("MYDB");
pConnection->setAutoCommit(0);
sql::ResultSet* pResultSet;
pPreparedStatement = pConnection->prepareStatement ("select * from mytable where mycolumn > ?"); // mytable has mycolumn that contains 1000 numbers starting from 1
pPreparedStatement->setInt(1, 1);
pResultSet= pPreparedStatement->executeQuery();
int count = pResultSet->rowsCount();
printf("\nTotal rows found %d", count);
delete pResultSet;
// delete pPreparedStatement; // Let's not delete prepared statement to see demo of memory leak in pResultSet
delete pConnection;
printf ("\nDone! Quitting...");
return 0;
}
这是报告:
Visual Leak Detector Version 2.4RC2 installed.
Aggregating duplicate leaks.
Suppressing data dumps.
Outputting the report to E:\MySQL\memory_leak_report.txt
WARNING: Visual Leak Detector detected memory leaks!
---------- Block 65 at 0x0000000068D87EB0: 8 bytes ----------
Leak Hash: 0x38615834, Count: 1, Total 8 bytes
Call Stack (TID 4628):
0x00000000C3EC5630 (File and line number not available): ntdll.dll!RtlAllocateHeap
f:\dd\vctools\crt_bld\self_64_amd64\crt\src\new.cpp (59): MySQLTrials.exe!operator new + 0xA bytes
0x00000000DF30AEE4 (File and line number not available): MySQLTrials.exe!sql::mysql::util::Singleton<sql::mysql::NativeAPI::LibmysqlStaticProxy>::theInstance + 0x44 bytes
0x00000000DF306DB1 (File and line number not available): MySQLTrials.exe!sql::mysql::NativeAPI::getCApiHandle + 0x41 bytes
0x00000000DF2AA5AC (File and line number not available): MySQLTrials.exe!sql::mysql::NativeAPI::MySQL_NativeDriverWrapper::MySQL_NativeDriverWrapper + 0x5C bytes
0x00000000DF2AA51D (File and line number not available): MySQLTrials.exe!sql::mysql::NativeAPI::createNativeDriverWrapper + 0x4D bytes
0x00000000DF28401B (File and line number not available): MySQLTrials.exe!sql::mysql::MySQL_Driver::MySQL_Driver + 0x8B bytes
0x00000000DF28456F (File and line number not available): MySQLTrials.exe!sql::mysql::get_driver_instance_by_name + 0x18F bytes
0x00000000DF284681 (File and line number not available): MySQLTrials.exe!sql::mysql::get_driver_instance + 0x21 bytes
0x00000000DF283E1A (File and line number not available): MySQLTrials.exe!get_driver_instance + 0x1A bytes
e:\mysql\mysql.cpp (22): MySQLTrials.exe!wmain + 0x5 bytes
f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crt0.c (240): MySQLTrials.exe!__tmainCRTStartup + 0x19 bytes
f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crt0.c (164): MySQLTrials.exe!wmainCRTStartup
0x00000000C1CF167E (File and line number not available): KERNEL32.DLL!BaseThreadInitThunk + 0x1A bytes
0x00000000C3EDC3F1 (File and line number not available): ntdll.dll!RtlUserThreadStart + 0x21 bytes
---------- Block 413 at 0x0000000068D90FF0: 40 bytes ----------
Leak Hash: 0x7614B12C, Count: 1, Total 40 bytes
Call Stack (TID 4628):
0x00000000C3EC5630 (File and line number not available): ntdll.dll!RtlAllocateHeap
f:\dd\vctools\crt_bld\self_64_amd64\crt\src\new.cpp (59): MySQLTrials.exe!operator new + 0xA bytes
0x00000000DF30C576 (File and line number not available): MySQLTrials.exe!sql::mysql::NativeAPI::MySQL_NativeConnectionWrapper::stmt_init + 0x86 bytes
0x00000000DF28E730 (File and line number not available): MySQLTrials.exe!sql::mysql::MySQL_Connection::prepareStatement + 0xC0 bytes
e:\mysql\mysql.cpp (30): MySQLTrials.exe!wmain + 0x30 bytes
f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crt0.c (240): MySQLTrials.exe!__tmainCRTStartup + 0x19 bytes
f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crt0.c (164): MySQLTrials.exe!wmainCRTStartup
0x00000000C1CF167E (File and line number not available): KERNEL32.DLL!BaseThreadInitThunk + 0x1A bytes
0x00000000C3EDC3F1 (File and line number not available): ntdll.dll!RtlUserThreadStart + 0x21 bytes
---------- Block 241 at 0x0000000068D93910: 16 bytes ----------
Leak Hash: 0x447A29BE, Count: 1, Total 16 bytes
Call Stack (TID 4628):
0x00000000C3EC5630 (File and line number not available): ntdll.dll!RtlAllocateHeap
c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0 (592): MySQLTrials.exe!std::allocator<std::_Container_proxy>::allocate
0x00000000DF28B052 (File and line number not available): MySQLTrials.exe!std::_Wrap_alloc<std::allocator<std::_Container_proxy> >::allocate + 0x32 bytes
0x00000000DF303CA7 (File and line number not available): MySQLTrials.exe!std::_Deque_alloc<0,std::_Deque_base_types<sql::mysql::MySQL_DebugEnterEvent const * __ptr64,std::allocator<sql::mysql::MySQL_DebugEnterEvent const * __ptr64> > >::_Alloc_proxy + 0x37 bytes
0x00000000DF303991 (File and line number not available): MySQLTrials.exe!std::_Deque_alloc<0,std::_Deque_base_types<sql::mysql::MySQL_DebugEnterEvent const * __ptr64,std::allocator<sql::mysql::MySQL_DebugEnterEvent const * __ptr64> > >::_Deque_alloc<0,std::_Deque_base_types<sql::mysql::MySQL_DebugEnterEvent const * __ptr64,std + 0x41 bytes
0x00000000DF303A95 (File and line number not available): MySQLTrials.exe!std::deque<sql::mysql::MySQL_DebugEnterEvent const * __ptr64,std::allocator<sql::mysql::MySQL_DebugEnterEvent const * __ptr64> >::deque<sql::mysql::MySQL_DebugEnterEvent const * __ptr64,std::allocator<sql::mysql::MySQL_DebugEnterEvent const * __ptr64> > + 0x35 bytes
0x00000000DF303ACB (File and line number not available): MySQLTrials.exe!std::stack<sql::mysql::MySQL_DebugEnterEvent const * __ptr64,std::deque<sql::mysql::MySQL_DebugEnterEvent const * __ptr64,std::allocator<sql::mysql::MySQL_DebugEnterEvent const * __ptr64> > >::stack<sql::mysql::MySQL_DebugEnterEvent const * __ptr64,std::d + 0x2B bytes
0x00000000DF302AFE (File and line number not available): MySQLTrials.exe!sql::mysql::MySQL_DebugLogger::MySQL_DebugLogger + 0x3E bytes
0x00000000DF28CD77 (File and line number not available): MySQLTrials.exe!sql::mysql::MySQL_Connection::MySQL_Connection + 0x227 bytes
0x00000000DF284184 (File and line number not available): MySQLTrials.exe!sql::mysql::MySQL_Driver::connect + 0xA4 bytes
e:\mysql\mysql.cpp (23): MySQLTrials.exe!wmain + 0x5B bytes
f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crt0.c (240): MySQLTrials.exe!__tmainCRTStartup + 0x19 bytes
f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crt0.c (164): MySQLTrials.exe!wmainCRTStartup
0x00000000C1CF167E (File and line number not available): KERNEL32.DLL!BaseThreadInitThunk + 0x1A bytes
0x00000000C3EDC3F1 (File and line number not available): ntdll.dll!RtlUserThreadStart + 0x21 bytes
---------- Block 483 at 0x0000000068D93960: 11 bytes ----------
Leak Hash: 0x1D599652, Count: 1, Total 11 bytes
Call Stack (TID 4628):
0x00000000C3EC5630 (File and line number not available): ntdll.dll!RtlAllocateHeap
f:\dd\vctools\crt_bld\self_64_amd64\crt\src\newaop.cpp (7): MySQLTrials.exe!operator new[]
0x00000000DF32199C (File and line number not available): MySQLTrials.exe!sql::mysql::MySQL_ResultBind::bindResult + 0xA0C bytes
0x00000000DF321379 (File and line number not available): MySQLTrials.exe!sql::mysql::MySQL_ResultBind::bindResult + 0x3E9 bytes
0x00000000DF313F69 (File and line number not available): MySQLTrials.exe!sql::mysql::MySQL_Prepared_ResultSet::MySQL_Prepared_ResultSet + 0x169 bytes
0x00000000DF2EC0E1 (File and line number not available): MySQLTrials.exe!sql::mysql::MySQL_Prepared_Statement::executeQuery + 0x1F1 bytes
e:\mysql\mysql.cpp (33): MySQLTrials.exe!wmain + 0x13 bytes
f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crt0.c (240): MySQLTrials.exe!__tmainCRTStartup + 0x19 bytes
f:\dd\vctools\crt_bld\self_64_amd64\crt\src\crt0.c (164): MySQLTrials.exe!wmainCRTStartup
0x00000000C1CF167E (File and line number not available): KERNEL32.DLL!BaseThreadInitThunk + 0x1A bytes
0x00000000C3EDC3F1 (File and line number not available): ntdll.dll!RtlUserThreadStart + 0x21 bytes
Visual Leak Detector detected 119 memory leaks (640915 bytes).
Largest number used: 697643 bytes.
Total allocations: 837447 bytes.
Visual Leak Detector is now exiting.
题:
为什么我们在MySQL.cpp(23)处看到泄漏:
pConnection = driver->connect("tcp://127.0.0.1:3306", "username", "password");
和MySQL.cpp(33)
pResultSet= m_pPreparedStatement->executeQuery();
尽管我们删除pResultSet
and pConnection
?为什么我们需要删除pPreparedStatement
以及免费结果集?
我可以从你的陈述中推断出,你已经释放了与声明的联系:
delete pConnection
这意味着当你再次初始化PreparedStatement
时,你必须将其初始化为:
pPreparedStatement = pConnection->prepareStatement(...)
因此,您不要释放PreparedStatement
对象,而只是重用该对象。默认情况下,C ++没有垃圾收集,当您不再需要它们时,您必须释放对象。
为了有一些接近Java垃圾收集的东西,我建议你使用shared_ptr
或scoped_ptr
,它将在你的对象不再需要时释放你的记忆。
您可以查看Boost C++ Libraries以获取有关如何动态释放内存的全面教程。例如:
boost::scoped_ptr<sql::Connection> con(driver->connect(host, user,pass));
boost::scoped_ptr<sql::Statement> stmt(con->createStatement());
这样,您不需要记住在对象上调用delete,因为当对象不再在范围内时,将释放内存。但是你必须包括boost库。
#include <boost/scoped_ptr.hpp>
在我看来,显而易见的是,预处理语句包含用于检索结果集的查询字符串,它表示我看起来准备好的语句位于内存的单独位置而不是结果集,您也可以从中推断出码:
sql::ResultSet *pResultSet = NULL;
sql::PreparedStatement *pPreparedStatement = NULL;
因此,当连接超出范围或被释放时,为预准备语句分配的内存仍然与包含该查询的对象相关联,但由于释放了连接对象,因此您无法访问该部分内存,并且因为它没有像你一样被释放你的操作系统仍然标记了那部分内存标记为正在使用,导致内存泄漏。
为什么要经历所有麻烦,找出为什么你已经释放内存以防止内存泄漏,这对我来说显而易见,而不是只添加一行代码:
delete pPreparedStatement.
如随附文档中所述,或者我喜欢这样做,将其封装在一个类(例如dbConnection)中,让de析构函数完成所有工作,这样你只需要在一个位置编写语句就可以在之后忘记它。这是简单而干净的练习,它教会编写干净无泄漏的代码。
通过额外的库添加垃圾收集只会让你邋and并且在我看来制造有缺陷的软件,它通常不值得麻烦并且可以增加大量开销,而你真的需要为每个对象添加一行代码来删除。此外,我认为当一些垃圾被删除时,这种懒惰最终会让你陷入困境,同时仍然需要它所包含的数据,但是垃圾收集很早就会发生,或者实际上你让你的对象很快就会超出范围你必须找出你的应用程序在特定情况下不断崩溃的原因。当你自己完成所有的工作时,它可能在开始时看起来更麻烦,但最后你会很高兴你可以在几秒钟内纠正这种编码错误,而不是长时间对抗bug寻找只会找到你的某个地方代码中的一个小错误。
选择您正在使用的MySQL版本并搜索“准备好的SQL语句语法”。
您将找到3个特定活动和语法示例A)PREPARE语法B)EXECUTE语法C)DEALLOCATE语法
这可能比ResultSet对象删除更有效,并且当DEALLOCATE完成时,您可能会看到您的资源已被释放。