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

Why is this simple libpqxx code leaking memory?

本文关键字:代码 泄漏 内存 libpqxx 简单 为什么      更新时间:2023-10-16

最近我写了一个应用程序,通过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 %dn", i );
            r.clear();
        } //while
    } //try
    catch (const std::exception &e)
    {
        std::cerr << e.what() << std::endl;
        return 1;
    } //catch
} //main

在大约75000个循环之后,顶部显示了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 4.4.0版
  • libpqxx 4.0
  • postgres 9.2

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

我无法重现您的问题。

无论是libpqxx-2.6.9还是libpqxxx-4.0.1(此时为当前稳定版本)。

  • Debian Linux 2.6.32
  • gcc版本4.4.5
  • libpq5 8.4.17
  • libpqxx 4.0.1
  • libstdc++6.4.4
  • 后期8.4

内存使用率是恒定的。

$valgrind--tool=memcheck run.sh
[…]
周期35000
周期36000
^C===18420===
===18420===治疗总结:
===18420===出口时使用:0个块中有0个字节
===18420===总堆使用率:0个分配,0个释放,0个分配字节
===18420===
==18420==所有堆块都已释放--不可能发生泄漏

如果您对正在使用的库的不同安装有问题,我会进行检查。例如,您是否链接到libpqxx-4.0,但稍后运行程序时,您将使用不同的版本。

$ldd客户端
linux gate.so.1=>(0xb7776000)
libpqxx-4.0.so=>未找到
libstdc++.so.6=>/usr/lib/libstdc++.so.6(0xb766900)
libgcc_s.so.1=>/lib/libgcc_s.so.1(0xb764a000)
libc.so.6=>/lib/i686/cmov/libc.so.6(0xb7503000)libpthread.so.0=>/lib/i686/cmov/libpthread.so.0(0xb74ea000)
libm.so.6=>/lib/i686/cmov/libm.so.6(0xb74c4000)
/lib/ld-linux.so.2(0xb777700)

由于我已经手动下载并安装了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++

客户:客户.o

libpqxx是用构建的

/configure--prefix=/home/dev/data/src/cpp/libpqxx-4.0.1/install--启用共享
制作
制作安装

我已经用运行了这个程序

LD_LIBRARY_PATH=$LD_LIBRRAY_PATH:/home/dev/data/src/cpp/libpqxx-4.0.1/install/lib客户端

很可能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 %dn", i );
            r.clear();
        } //while
    } //try
    catch (const std::exception &e)
    {
        std::cerr << e.what() << std::endl;
        return 1;
    } //catch
} //main

尝试声明pqxx::result r while。把它称为内部,就像你正在做的那样。因为对于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 %dn", 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();
    }
}