2010-12-01 15 views
1

我使用QT 4.5.3訪問SQLite數據庫,就像這樣:打開數據庫導致內存泄漏

class db : private boost::noncopyable 
{ 
public: 
    db(QString file) : filename(file), 
         realdb(NULL), 
         theConnectionEstablished(false) 
    { 
    } 
    ~db() 
    { 
    if (NULL != realdb.get()) 
    { 
     realdb.reset(NULL); 
    } 
    if (theConnectionEstablished) 
    { 
     QSqlDatabase::removeDatabase("ConnName"); 
    } 
    } 

    void open() 
    { 
    realdb.reset(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", "ConnName"))); 
    theConnectionEstablished = true; 

    // open the db 
    realdb->setDatabaseName(filename); 
    if (! realdb->open()) 
    { 
     const QSqlError dbError = realdb->lastError(); 
     const QString errorDesc = "Error opening the database : " + filename + 
            "\nDatabase error : " + dbError.databaseText() + 
            "\nDatabase driver error : " + dbError.driverText(); 

     // DatabaseError is a class type which accepts the std::string for logging purposes 
     throw DatabaseError(errorDesc.toStdString()); 
    } 
    } 

    const QString filename; 
    std::auto_ptr<QSqlDatabase> realdb; 
    bool theConnectionEstablished; 
}; 

現在,如果我嘗試測試這種情況下,像這樣(我使用cxxtest) :

void test_failed_connection() 
{ 
    db obj("/"); 
    TS_ASSERT_THROWS(obj.open(), DatabaseError); 
} 

我得到的valgrind報告內存泄漏:

<error> 
    <unique>0x5b</unique> 
    <tid>1</tid> 
    <kind>Leak_DefinitelyLost</kind> 
    <what>986 (384 direct, 602 indirect) bytes in 1 blocks are definitely lost in loss record 23 of 23</what> 
    <leakedbytes>986</leakedbytes> 
    <leakedblocks>1</leakedblocks> 
    <stack> 
    <frame> 
     <ip>0x4006D3E</ip> 
     <obj>/opt/valgrind341/lib/valgrind/x86-linux/vgpreload_memcheck.so</obj> 
     <fn>malloc</fn> 
     <dir>/home/slawomir/valgrind-3.4.1/build/valgrind-3.4.1/coregrind/m_replacemalloc</dir> 
     <file>vg_replace_malloc.c</file> 
     <line>207</line> 
    </frame> 
    <frame> 
     <ip>0x67FADC4</ip> 
     <obj>/usr/lib/libsqlite3.so.0.8.6</obj> 
     <fn>sqlite3_malloc</fn> 
    </frame> 
    <frame> 
     <ip>0x67FAF13</ip> 
     <obj>/usr/lib/libsqlite3.so.0.8.6</obj> 
    </frame> 
    <frame> 
     <ip>0x6816DA3</ip> 
     <obj>/usr/lib/libsqlite3.so.0.8.6</obj> 
    </frame> 
    <frame> 
     <ip>0x68175FD</ip> 
     <obj>/usr/lib/libsqlite3.so.0.8.6</obj> 
     <fn>sqlite3_open16</fn> 
    </frame> 
    <frame> 
     <ip>0x40DDEF9</ip> 
     <obj>/usr/lib/qt4/plugins/sqldrivers/libqsqlite.so</obj> 
    </frame> 
    <frame> 
     <ip>0x7F34AE0</ip> 
     <obj>/usr/lib/libQtSql.so.4.5.2</obj> 
     <fn>QSqlDatabase::open()</fn> 
    </frame> 
    </frame> 
    </stack> 
</error> 

有誰知道如何解決此泄漏?

+0

@DumbCode我不想優化任何東西,但我正在嘗試單元測試我的代碼。問題是如何在打開數據庫出現問題時測試案例。正如您在示例中所看到的,我傳遞「/」(根目錄)作爲數據庫名稱來模擬該情況。 – 2010-12-01 10:17:55

回答

3

有一個瀏覽Qt和sqlite的來源...有趣的。

閱讀sqlite3_open16()http://www.sqlite.org/c3ref/open.html說明書,可以發現以下報價:

無論它打開時,與數據庫連接句柄相關聯的資源應該被釋放時發生錯誤的把它傳遞給sqlite3_close ()當它不再需要時。

QSQLiteDriver::close()似乎調用該,http://qt.gitorious.org/qt/qt/blobs/4.7/src/sql/drivers/sqlite/qsql_sqlite.cpp,只有在打開的情況下,具有了成功。 SQLite的文檔可能表明sqlite3_close()應該被調用。

另一方面,http://www.sqlite.org/c3ref/close.html聲稱如果NULL被傳遞給一個句柄(如果打開失敗,則會被視爲無操作)。查看SQLite源代碼(DIY - 我不知道它的web源代碼瀏覽器界面)確認,如果用NULL調用,它只會返回。

好了 - 現在

天真問題的基本事實的樂趣......,一個假定的sqlite3_open*()失敗將意味着NULL分貝手柄。但根據SQLite的消息來源,在main.c中閱讀openDatabase(),事實並非如此 - 該調用可能會失敗,但仍會返回非db2句柄NULL

Qt看起來像是假設打開數據庫連接失敗意味着收到一個NULL數據庫句柄。但這不是SQLite所做的。不過,文檔可能會更清晰。

嘗試添加近QSQLiteDriver::open(),看看它是否修復泄漏。如果是這樣,請向Qt工作人員報告錯誤,並與SQLite人員一起澄清文檔;-)

0

您的代碼

realdb.reset(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", "ConnName")));

看起來有些奇怪,不知道爲什麼它編譯。我沒有看到QSqlDatabase的構造函數將QSqlDatabase *作爲參數。

您調用QSqlDatabase :: addDatabase,它將返回QSqlDatabase *,然後使用new構造另一個QSqlDatabase並將其作爲參數傳遞。

相反的auto_ptr的,你可以使用boost :: shared_ptr的,然後用

realdb.reset(QSqlDatabase::addDatabase("QSQLITE", "ConnName"), QSqlDatabase::removeDatabase);

重置要注意的是removeDatabase可以留下一個資源泄漏,如果有開放的查詢就可以了。

+0

再看一遍。 auto_ptr的構造函數需要指針。在open()方法中,我打開連接並嘗試打開數據庫。在析構函數中,我破壞了QSqlDatabase對象,然後刪除連接,因此沒有資源泄漏。 – 2010-12-01 11:03:08

+0

我忘了提及這個類不應該是可複製的(它繼承自boost :: noncopyable),因此在這種情況下使用shared_ptr與使用auto_ptr相同。 – 2010-12-01 11:05:22