为什么立即销毁共享指针会泄漏内存?

时间:2012-02-22 06:39:53

标签: c++ boost-asio smart-pointers

这里是否有内存泄漏?

class myclass : public boost::enable_shared_from_this<myclass>{
//...
    void broadcast(const char *buf){
    broadcast(new std::string(buf));
    }

    void broadcast(std::string *buf){
    boost::shared_ptr<std::string> msg(buf);
    }
//...
};  

(这是仍然显示问题的精简版本 - 通常我会在第二次broadcast电话中真正开展工作!) 我的假设是第一次调用得到一些内存,然后因为我对智能指针什么都不做,第二次调用会立即删除它。简单?但是,当我运行程序时,内存会随着时间的推移而增加。然而,当我将程序中唯一的调用注释为broadcast()时,它没有!

没有broadcast()的版本的ps输出:

 %CPU  %MEM     VSZ    RSS  TIME
 3.2   0.0     158068  1988 0:00 
 3.3   0.0     158068  1988 0:25  (12 mins later)

调用broadcast()(在Ubuntu 10.04上,g ++ 4.4,增强1.40)

 %CPU  %MEM     VSZ    RSS  TIME
 1.0    0.0    158068  1980 0:00
 3.3    0.0    158068  1988 0:04  (2 mins)
 3.4    0.0    223604  1996 0:06  (3.5 mins)
 3.3    0.0    223604  2000 0:09
 3.1    0.0    223604  2000 2:21  (82 mins)
 3.1    0.0    223604  2000 3:50  (120 mins)

(看到在3分钟左右的跳跃在我迄今为止尝试过的几次中是可重现的。)

调用broadcast()(在Centos 5.6上,g ++ 4.1,提升1.41)

 %CPU  %MEM     VSZ    RSS  TIME
 0.0    0.0     51224  1744 0:00
 0.0    0.0     51224  1744 0:00  (30s)
 1.1    0.0    182296  1776 0:02  (3.5 mins)
 0.7    0.0    182296  1776 0:03
 0.7    0.0    182296  1776 0:09  (20 mins)
 0.7    0.0    247832  1788 0:14  (34 mins)
 0.7    0.0    247832  1788 0:17
 0.7    0.0    247832  1788 0:24  (55 mins)
 0.7    0.0    247832  1788 0:36  (71 mins)

以下是调用broadcast()的方法(来自boost :: asio计时器),现在我想知道它是否重要:

void callback(){
//...
timer.expires_from_now(boost::posix_time::milliseconds(20));
//...
char buf[512];
sprintf(buf,"...");
broadcast(buf);
timer.async_wait(boost::bind(&myclass::callback, shared_from_this() ));
//...
}

(回调与广播功能属于同一类)

我有4个这样的计时器,我的io_service.run()被3个线程的池调用。我的20ms超时意味着每个计时器调用broadcast() 50次/秒。我在函数开始时设置了到期时间,并在接近结束时运行计时器。遗漏的代码并没有做那么多;将调试信息输出到std :: cout可能是CPU占用最多的工作。我想有时可能会立即触发计时器;但是,我仍然看不出这会是一个什么问题,更不用说造成内存泄漏了。

(顺便说一句,该程序运行正常,即使在执行完整任务时也是如此;当我注意到ps报告的内存使用率上升时,我只会怀疑。)

更新:感谢您的回答和评论。我可以补充一点,我让程序在每个系统上运行了几个小时,内存使用量没有进一步增加。 (当Centos版本第二次跳跃时,我也准备将其视为一次性堆重组或其他东西。)无论如何,我很高兴知道我对智能指针的理解仍然是合理的,并且有没有我需要关注的带有线程的怪异角落案例。

2 个答案:

答案 0 :(得分:2)

如果发生泄漏,则每秒50次分配std::string(20字节,或多或少)。 在1小时内你应该被分配... 3600 * 50 * 20 = 3,4MBytes。

与你看到的64K没什么关系,这可能是由于系统将内存分配给进程的方式,new子分配给变量。

当系统被删除时,系统需要“垃圾收集它”,将其放回可用的内存链中以进行进一步的分配。 但由于这需要时间,因此大多数系统在释放的内存超过一定数量之前不会执行此操作,因此可以执行“重新包装”。

答案 1 :(得分:1)

嗯,这里发生的事情可能不是你的程序泄漏,但由于某种原因,系统内存分配器决定为你的应用程序保留另一个64 kB的页面。如果此时存在持续的内存泄漏,以50 Hz的速率,则会产生更大的影响!

究竟为什么在3分钟之后我不知道(我不是该领域的专家),但我猜想有一些启发式和统计数据。或者,它可能只是堆已经碎片化。

可能发生的另一件事是,您在缓冲区中保存的消息会随着时间的推移变得更长:)