4

最近我写了一个通过 libpqxx 访问 Postgres DB 的应用程序,它严重地泄漏了内存。即使是这个基于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 版本 4.4.0
  • libpqxx 4.0
  • postgres 9.2

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

4

3 回答 3

3

我无法重现您的问题。

既不使用 libpqxx-2.6.9 也不使用 libpqxx-4.0.1(此时为当前稳定版本)。

  • Debian Linux 2.6.32
  • gcc 版本 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== 堆摘要:
==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 (0xb7669000)
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 (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
make install

我已经使用

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

于 2013-07-03T08:32:26.763 回答
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
于 2013-07-02T18:00:10.103 回答
1

尝试在一段时间内声明 pqxx::result r。并在里面调用它,就像你正在做的那样。因为对于 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();
    }
}
于 2014-05-12T15:11:35.077 回答