为什么这个简单的libpqxx代码会泄漏内存?

时间:2013-07-02 17:41:52

标签: c++ postgresql memory-leaks libpqxx

最近我写了一个通过libpqxx访问Postgres数据库的应用程序,它会严重泄漏内存。即使是这个基于http://pqxx.org/devprojects/libpqxx/doc/4.0/html/Reference/a00001.html示例的简单测试程序,也就像没有明天一样泄漏。

(编辑:我添加了对commit()和clear()的调用以响应建议。相同的漏洞。)

#include <iostream>
#include <pqxx/pqxx>
#include <string>
#include <stdio.h>

int main()
{
    try
    {
        pqxx::connection c("user=postgres");

        int i = 0;          
        while(true)
        {
            pqxx::work w(c);
            pqxx::result r = w.exec("SELECT 1");
            w.commit();

            i++;
            if ( i % 1000 == 0 )
                printf( "Cycle %d\n", i );

            r.clear();
        } //while
    } //try
    catch (const std::exception &e)
    {
        std::cerr << e.what() << std::endl;
        return 1;
    } //catch
} //main

在循环的大约75,000个循环之后,top显示了206Mb的虚拟内存使用量,并且它不断攀升。我通过valgrind运行了5000个循环的类似测试程序并得到了这个:

==1647== 13,732,155 (219,868 direct, 13,512,287 indirect) bytes in 4,997 blocks are definitely lost in loss record 12 of 12
==1647==    at 0x40060D5: operator new(unsigned int) (vg_replace_malloc.c:214)
==1647==    by 0x404C0A9: pqxx::result::result(pg_result*, int, std::string const&, int) (in /usr/lib/libpqxx-4.0.so)
==1647==    by 0x40309EF: pqxx::connection_base::make_result(pg_result*, std::string const&) (in /usr/lib/libpqxx-4.0.so)
==1647==    by 0x4036D65: ??? (in /usr/lib/libpqxx-4.0.so)
==1647==    by 0x405EFD6: pqxx::transaction_base::DirectExec(char const*, int) (in /usr/lib/libpqxx-4.0.so)
==1647==    by 0x40416EA: pqxx::dbtransaction::do_exec(char const*) (in /usr/lib/libpqxx-4.0.so)
==1647==    by 0x40618FA: pqxx::transaction_base::exec(std::string const&, std::string const&) (in /usr/lib/libpqxx-4.0.so)
==1647==    by 0x80498F8: main (dbtest.cpp:21)

知道发生了什么事吗?很难接受像libpqxx这样广泛使用的库会有如此严重的错误,所以我在这里做错了什么呢?

配置详情:

  • 操作系统:Linux 2.6.18-238.el5
  • gcc version 4.4.0
  • libpqxx 4.0
  • postgres 9.2

最终编辑:我发现用libpq替换libpqxx比继续调查内存泄漏更容易。

3 个答案:

答案 0 :(得分:3)

我无法重现你的问题。

既不使用libpqxx-2.6.9也不使用libpqxx-4.0.1(目前是当前的稳定版本)。

  • Debian Linux 2.6.32
  • gcc version 4.4.5
  • libpq5 8.4.17
  • libpqxx 4.0.1
  • libstdc ++ 6.4.4
  • postgres 8.4

内存使用量不变。

  

$ valgrind --tool = memcheck run.sh
  [...]
  循环35000
  循环36000
  ^ C == 18420 ==
  == 18420 == HEAP SUMMARY:
  == 18420 ==在退出时使用:0块中的0字节
  == 18420 ==总堆使用量:0个分配,0个释放,0个字节分配
  == == 18420
  == 18420 ==所有堆块都被释放 - 没有泄漏可能

如果您使用的库的安装不同,我会检查一下。例如,您是否链接到libpqxx-4.0,但稍后,当您运行该程序时,您使用的是其他版本。

  

$ ldd client
  linux-gate.so.1 =&gt; (0xb7776000)
  libpqxx-4.0.so =&gt;没找到
  libstdc ++。so.6 =&gt; /usr/lib/libstdc++.so.6(0xb7669000)
  libgcc_s.so.1 =&gt; /lib/libgcc_s.so.1(0xb764a000)
  libc.so.6 =&gt; /lib/i686/cmov/libc.so.6(0xb7503000)   libpthread.so.0 =&gt; /lib/i686/cmov/libpthread.so.0(0xb74ea000)
  libm.so.6 =&gt; /lib/i686/cmov/libm.so.6(0xb74c4000)
  /lib/ld-linux.so.2(0xb7777000)

由于我已手动下载并安装了libpqxx-4.0.1,因此我必须明确设置LD_LIBRARY_PATH,因为我尚未在/usr/local/下面安装它。如果在/usr/local中安装了不同的版本,则应在之前进行清理。

在您的情况下(使用ldd),libpqxx应指向您已编译和安装的版本。确保系统上只安装了一个libpqxx。

我使用了这个Makefile

  

CPPFLAGS + = -I / home / dev / data / src / cpp / libpqxx-4.0.1 / install / include
  LDFLAGS + = -L / home / dev / data / src / cpp / libpqxx-4.0.1 / install / lib -lpqxx -lstdc ++

     

客户端:client.o

和libpqxx已经用

构建
  

./ configure --prefix = / home / dev / data / src / cpp / libpqxx-4.0.1 / install --enable-shared
  使
  make install

我已经使用

运行程序了
  

LD_LIBRARY_PATH = $ LD_LIBRARY_PATH:/home/dev/data/src/cpp/libpqxx-4.0.1/install/lib client

答案 1 :(得分:1)

pqxx :: result可能泄漏了内存。尝试添加:

r.clear();

至少可以减少泄漏。

#include <iostream>
#include <pqxx/pqxx>
#include <string>
#include <stdio.h>

int main()
{
    try
    {
        pqxx::connection c("user=postgres");

        int i = 0;  

        while(true)
        {
            pqxx::work w(c);
            pqxx::result r = w.exec("SELECT 1");

            i++;
            if ( i % 1000 == 0 )
                printf( "Cycle %d\n", i );

            r.clear();
        } //while
    } //try
    catch (const std::exception &e)
    {
        std::cerr << e.what() << std::endl;
        return 1;
    } //catch
} //main

答案 2 :(得分:1)

尝试声明pqxx :: result r whitout。就像你在做的那样把它称为内部。因为对于while的每个循环,您正在创建一个pqxx :: result对象。通过这种方式,您将为所有循环创建一个对象,在每个循环中充电和清除:

#include <iostream>
#include <pqxx/pqxx>
#include <string>
#include <stdio.h>

int main()
{
    try
    {
        pqxx::connection c("user=postgres");

        int i = 0;          
        pqxx::result r;
        while(true)
        {
            pqxx::work w(c);
            r = w.exec("SELECT 1");
            w.commit();

            i++;
            if ( i % 1000 == 0 )
                printf( "Cycle %d\n", i );

            r.clear();
        } //while
    } //try
    catch (const std::exception &e)
    {
        std::cerr << e.what() << std::endl;
        return 1;
    } //catch
} //main

或者甚至更好,创建一个方法来调用while的每个循环,因此,当每次调用方法完成时,临时对象就会消失。

main()
{
    ...
    while()
    {
        ...
        method_to_call_pqxx();
    }
}