避免新的(new [])引起的内存泄漏

时间:2017-10-06 13:53:43

标签: c++ memory memory-leaks smart-pointers

我正在开发一个内存泄漏的开源库。该库是围绕boost :: asio构建的数据流服务。服务器端使用堆内存管理系统,该系统提供内存以在等待通过tcp连接推送时保持有限数量的samples。首次构造服务器时,将分配所有旧sample的内存堆。在这个堆中,在套接字传递sample之后,内存将返回到堆中。

这很好,除非已经采用了所有预先分配的堆。以下是创建'样本':

的功能
sample_p new_sample(double timestamp, bool pushthrough) { 
    sample *result = pop_freelist();
    if (!result){
        result = new(new char[sample_size_]) sample(fmt_, num_chans_, this);
    }
    return sample_p(result);
}

sample_p只是一个模板化为sample类的typedef智能指针。

违规线在中间。如果freelist上没有一块内存,我们需要制作一些内存。这会泄漏内存。

我的问题是为什么会发生这种情况?由于我将新样本推送到智能指针中,因此当内存超出范围时,内存不应被释放(稍后会从堆栈中弹出)。我是否需要以某种方式处理内部分配的内存 - 即。 new char[sample_size_]分配的内存?如果是,我该怎么做?

编辑: @RichardHodges这里是一个可编译的MCVE。这是高度简化的,但我认为它完全捕获了我在原始代码中遇到的问题。

#include <boost/intrusive_ptr.hpp>
#include <boost/lockfree/spsc_queue.hpp>
#include <iostream>

typedef boost::intrusive_ptr<class sample> sample_p;
typedef boost::lockfree::spsc_queue<sample_p> buffer;
class sample {

public:
    double data;
    class factory{
    public:
        friend class sample;
        sample_p new_sample(int size, double data) {
            sample* result = new(new char[size]) sample(data);
            return sample_p(result);
        }
    };

    sample(double d) {
        data = d;
    }

    void operator delete(void *x) {
        delete[](char*)x;
    }

    /// Increment ref count.
    friend void intrusive_ptr_add_ref(sample *s) {

    }

    /// Decrement ref count and reclaim if unreferenced.
    friend void intrusive_ptr_release(sample *s) {

    }

};


void push_sample(buffer &buff, const sample_p &samp) {

    while (!buff.push(samp)) {
        sample_p dummy;
        buff.pop(dummy);
    }
}

int main(void){
    buffer buff(1);
    sample::factory factory_;
    for (int i = 0; i < 10; i++)
        push_sample(buff, factory_.new_sample(100,0.0));
    std::cout << "press any key to exit" << std::endl;
    char foo;
    std::cin >> foo;

    return 0;
}

当我单步执行代码时,我注意到我的删除操作符永远不会被调用样本指针。我想我正在处理的图书馆(我再也没有写过,所以我还在学习它的方法)是错误地使用了intrusive_ptr类型。

1 个答案:

答案 0 :(得分:2)

您正在使用new[]分配内存,因此您需要使用delete[](在char*上)取消分配内存。智能指针可能默认调用delete,因此您应该提供一个调用delete[]的自定义删除器(在手动调用sample的析构函数之后)。以下是使用std::shared_ptr的示例。

auto s = std::shared_ptr<sample>(
  new (new char[sizeof(sample)]) sample,
  [](sample* p) {
    p->~sample();
    delete[] reinterpret_cast<char*>(p);
  }
);

但是,当缓冲区只包含一个对象时,为什么要使用展示位置new?为什么不直接使用常规new

auto s = std::shared_ptr<sample>(new sample);

甚至更好(使用std::shared_ptr),使用工厂函数。

auto s = std::make_shared<sample>();