简体   繁体   English

将sqlite3与C ++一起使用时发生内存泄漏

[英]Memory leak when using sqlite3 with C++

The program writes into SQLite database, the messages are received through a wireless module. 该程序将写入SQLite数据库,并通过无线模块接收消息。 But somehow there is a memory leak every time a message is received and written to the database, after about 10 000 writes the program is using 1GB of memory. 但是不知何故,每次接收到一条消息并将其写入数据库时​​,都会发生内存泄漏,在大约写入10 000之后,程序正在使用1 GB的内存。

The documentation for SQLite3 with C++ says that memory leaks are prevented with sqlite3_finalize() and sqlite3_close() which are present: 使用C ++的SQLite3的文档说,使用存在的sqlite3_finalize()sqlite3_close()可以防止内存泄漏:

#include <iostream>
#include <string>
#include <sstream>
#include "sqlite3.h"

using namespace std;

#define DB "test.db"
sqlite3 *dbfile;

bool connectDB();
void disonnectDB();
int insOrUpdate(string s);
int select(string s);

struct messageStruct_t {
  float value;
};

bool isOpenDB = false;

int main() {
  int counter = 0;
  while (1) {
    int header = 1;
    int message = rand() % 3;

    if (message) {
      counter ++;

      switch (header) {

      case 1: {
        messageStruct_t recMessage;
        recMessage.value = 55;
        int receivedSendersID = 2;

        //SQL query to get foreign key
        stringstream strm_select;
        strm_select << "SELECT id FROM table1 WHERE sendersID="
                    << receivedSendersID;
        string s_select = strm_select.str();
        cout << "SQL query: " << s_select << endl;
        int sendersID = select(s_select);
        cout << "Sender's ID: " << sendersID << endl;


        if (sendersID == 0) {
          cout << "Error: Sender doesn't exist\n";
        } else {
          stringstream strm_insert;
          strm_insert << "INSERT into table2(id,value,sender_id) values("
                      << counter << ", "
                      << recMessage.value << ", " << sendersID << ")";
          string s_insert = strm_insert.str();
          cout << "SQL query: " << s_insert << endl;
          insOrUpdate(s_insert);
          cout << "Recorded data: " << recMessage.value << endl;
        }
      }

      default: {
        break;
      }

      }
    }
  }
}

bool connectDB () {
  if (sqlite3_open(DB, &dbfile) == SQLITE_OK) {
    isOpenDB = true;
    return true;
  }
  return false;
}

void disonnectDB () {
  if ( isOpenDB == true ) {
    sqlite3_close(dbfile);
  }
}

int insOrUpdate(string s) {
  if (!connectDB()) {
    return 0;
  }

  char *str = &s[0];
  sqlite3_stmt *statement;
  int result;
  const char *query = str;

  if (sqlite3_prepare(dbfile, query, -1, &statement, 0) == SQLITE_OK) {
    result = sqlite3_step(statement);
    //the documentation says that this destroys the statement and prevents memory leaks
    sqlite3_finalize(statement);
    return result;
  }
  //and this destroys the db object and prevents memory leaks
  disonnectDB();
  return 0;
}

int select(string s) {
  if (!connectDB()) {
    return 0;
  }

  char *str = &s[0];
  sqlite3_stmt *statement;
  const char *query = str;
  string returned;
  if (sqlite3_prepare(dbfile, query, -1, &statement, 0) == SQLITE_OK) {
    int ctotal = sqlite3_column_count(statement);
    int res = 0;

    while (1) {
      res = sqlite3_step(statement);
      if (res == SQLITE_ROW) {
        for (int i = 0; i < ctotal; i++) {
          string s = (char*)sqlite3_column_text(statement, i);
          cout << s << " ";
          returned = s;
        }
        cout << endl;
      }
      if (res == SQLITE_DONE || res == SQLITE_ERROR) {
        cout << "done " << endl;
        break;
      }
    }
  } else {
    cout << "Can't prepare" << endl;
    return 0;
  }
  sqlite3_finalize(statement);
  disonnectDB();

  int result;
  stringstream convert(returned);
  if (!(convert >> result)) {
    result = 0;
  }

  return result;
}

CREATE TABLE table1 (
id INTEGER NOT NULL,
sendersID INTEGER,
PRIMARY KEY (id)
);
CREATE TABLE table2 (
id INTEGER NOT NULL,
value FLOAT,
sender_id INTEGER,
FOREIGN KEY(sender_id) REFERENCES table1 (id)
);

INSERT INTO table1(sendersID) values(2);

In your connectDB(..) call , you don't check if the database is already open before opening it again. 在connectDB(..)调用中,您无需在再次打开数据库之前检查数据库是否已打开。 Your memory leak is probably from the repeated mappings of this database into your memory space. 您的内存泄漏可能是由于该数据库重复映射到您的内存空间。

There may be other issues with this program but the change below to connectDB(..) should help with the leak on every successful insert. 该程序可能还存在其他问题,但对connectDB(..)进行的以下更改应有助于解决每次成功插入时的泄漏。

bool connectDB() {
    if (false == isOpenDB && sqlite3_open(DB, &dbfile) == SQLITE_OK) {
        isOpenDB = true;
    }
    return isOpenDB;
}

You should definitely use RAII for your connections and also for your statements. 您绝对应该使用RAII进行连接和声明。 There are several places where you return early without cleaning up statements and/or closing the connection. 在很多地方,您早早返回而没有清理语句和/或关闭连接。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM