2015-12-07 1 views
0

Dans mon application (en cours de développement sous Windows 8 avec MySQL Connector/C++), je crée des instructions préparées et ne les supprime qu'à la fin de l'application. Mais pendant l'exécution de l'application, j'exécute les requêtes et ne supprime que les ensembles de résultats.Instructions préparées: La mémoire reste allouée au jeu de résultats malgré sa suppression

Cependant, j'ai observé beaucoup de mémoire reste encore alloué et je l'ai senti plus que prévu. J'examine avec Visual Leak Detector et à ma grande surprise j'ai trouvé des fuites qui ont été montrées dans le pointeur de l'ensemble des résultats malgré que je les supprimais correctement. J'ai donc écrit un programme de démonstration qui fait exactement cela. C'est créer une instruction préparée, créer une requête, récupérer le résultat, supprimer le résultat (mais ne pas supprimer l'instruction préparée à la fin afin que nous puissions voir les fuites) et quitter. Voici le code de démonstration 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; 
} 

Et voici rapport:

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. 

Question:

Pourquoi voyons-nous des fuites à la ligne MySQL.cpp (23):

pConnection = driver->connect("tcp://127.0.0.1:3306", "username", "password"); 

et MySQL.cpp (3 3)

pResultSet= m_pPreparedStatement->executeQuery(); 

malgré nous supprimons pResultSet et pConnection? Pourquoi avons-nous besoin de supprimer pPreparedStatement ainsi que le jeu de résultats libre?

Répondre

1

Ce que je peux déduire de votre déclaration, vous libérez déjà la connexion avec l'énoncé:

delete pConnection 

Cela signifie que lorsque vous initialisez à nouveau le PreparedStatement, vous êtes devez l'initialiser comme:

pPreparedStatement = pConnection->prepareStatement(...) 

Par conséquent, vous ne libérez pas l'objet PreparedStatement mais réutilisez uniquement l'objet. C++ n'a pas de garbage collection par défaut, et vous devez libérer des objets lorsque vous n'en avez plus besoin. Afin d'avoir quelque chose de proche de la collecte des ordures de Java, je vous conseille d'utiliser un shared_ptr ou un scoped_ptr qui libérera votre mémoire lorsque vos objets ne seront plus nécessaires.

Vous pouvez consulter Boost C++ Libraries pour un didacticiel complet sur la façon de libérer de la mémoire dynamiquement. Par exemple:

boost::scoped_ptr<sql::Connection> con(driver->connect(host, user,pass)); 
boost::scoped_ptr<sql::Statement> stmt(con->createStatement()); 

De cette façon, vous n'avez pas besoin de se rappeler d'appeler delete sur vos objets en mémoire sera libérée lorsque l'objet n'est plus portée. Mais vous devez inclure des bibliothèques de boost.

#include <boost/scoped_ptr.hpp> 
+0

Je pense que l'auteur veut savoir pourquoi la mémoire de ResultSet reste allouée même lorsque l'objet ResultSet est supprimé. Le RAII n'est pas un problème ici. – gomons

+0

Et je veux le savoir aussi (= – gomons