std::shared_ptr

c++的std::shared_ptr,当同时创建几万个时,然后一起释放,为什么内存不会减少?
测试发现:当数量小于1万时能正常释放,当数量在4万时内存只释放一半,当数量为5万时内存全部不释放,内存没有释放的情况下再创建小于5万个智能指针时,内存不会增加

基于Monster 组和GPT的调写:

  • std::shared_ptr 是一种智能指针,用于管理动态分配的对象的生命周期。当所有指向一个对象的 shared_ptr 都被销毁时,该对象会被自动删除。但是,在创建大量 shared_ptr 的情况下,可能会发生内存泄漏的问题,即使 shared_ptr 已经被销毁,分配的内存可能仍然没有被释放。
  • 这种情况可能是由于 shared_ptr 内部维护了一个计数器,用于记录指向同一个对象的 shared_ptr 的数量。当计数器降为零时,shared_ptr 会自动释放分配的内存。但是,由于计数器是一个原子操作,当创建大量 shared_ptr 时,同步开销可能会变得很大,从而导致内存释放的延迟。
  • 另外,内存管理器也可能会影响 shared_ptr 的内存释放行为。一些内存管理器可能会在内存使用过多时延迟释放内存,以提高性能。这可能会导致 shared_ptr 释放内存的延迟。
  • 在你的测试中,当数量小于1万时,内存正常释放,这是因为计数器的同步开销和内存管理器的影响并不明显。但是,当数量增加到4万或5万时,同步开销变得更大,内存管理器的影响也变得更加明显,导致内存释放的延迟。
  • 如果你想确保在大量创建 shared_ptr 的情况下不会出现内存泄漏,可以考虑以下几种解决方案:
  • 使用 std::make_shared 函数,它可以在单次内存分配中同时分配 shared_ptr 对象和动态分配的对象,从而减少同步开销和内存管理器的影响。
  • 使用 std::shared_ptr 的重载构造函数,可以指定一个自定义的删除器,用于在所有 shared_ptr 都被销毁时释放动态分配的对象。
  • 使用其他的智能指针,如 std::unique_ptr,它不需要维护计数器,从而避免同步开销。
  • 避免同时创建大量 shared_ptr,尽量采用其他的内存管理技术,如内存池或对象池,来避免频繁的内存分配和释放。
  • 你可以看下这个问题的回答https://ask.csdn.net/questions/7425923
  • 你也可以参考下这篇文章:使用C++11解决内存泄露的问题(shared_ptr共享智能指针)
  • 除此之外, 这篇博客: shared_ptr 和线程安全的问题中的 std::shared_ptr的循环引用 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
    • 使用实例
    //shared_ptr 的循环引用
    #include<iostream>
    #include<memory>
    
    using namespace std;
    
    struct ListNode
    {
    int _data;
    shared_ptr<ListNode> _prev;
    shared_ptr<ListNode> _next;
    ~ListNode(){ cout << "~ListNode()" << endl; }
    };
    
    int main(){
    	shared_ptr<ListNode> node1(new ListNode);
    	shared_ptr<ListNode> node2(new ListNode);
    	cout << node1.use_count() << endl;
    	cout << node2.use_count() << endl;
    
    	node1->_next = node2;
    	node2->_prev = node1;
    
    	cout << node1.use_count() << endl;
    	cout << node2.use_count() << endl;
    	return 0;
    }
    
    

    在这里插入图片描述小黑:该函数中体现了shared_ptr的循环引用
    在两者之间建立了直接联系,如下图所示

    在这里插入图片描述
    在这里插入图片描述小辉:原来是将他们"首尾相连"了啊
    ,这样的话他们对彼此的依赖
    就变大了

    • 运行结果

    在这里插入图片描述
    在这里插入图片描述小辉:小黑,为啥两个结点在生命周期结束时
    没有调用析构函数啊?

    在这里插入图片描述小黑:小辉,你看看下面的分析你就知道了

    【循环引用分析】:

    • 1.node1和node2两个智能指针对象指向两个节点,引用计数变成1,我们不需要手动delete。
    • 2.node1的_next指向node2,node2的_prev指向node1,引用计数变成2。
    • 3.node1和node2析构,引用计数减到1,但是_next还指向下一个节点。但是_prev还指向上一个节点。
    • 4.也就是说_next析构了,node2就释放了。
    • 5.也就是说_prev析构了,node1就释放了。
    • 6.但是_next属于node的成员,node1释放了,_next才会析构,而node1由_prev管理,_prev属于node2成员,所以这就叫循环引用,谁也不会释放。

    【问题的出现】

    • 如何才能实现两者的析构呢?