网站的实施方案,wordpress调整时间,网站建设从哪几个情况去判,安徽省建设局网站查询内存泄露方法
啥是内存泄露
内存泄露在维基百科中的解释如下#xff1a; 在计算机科学中#xff0c;内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失#xff0c;而是应用程序分配某段内存后#xff0c;由于设计错误
在计算机科学中内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失而是应用程序分配某段内存后由于设计错误导致在释放该段内存之前就失去了对该段内存的控制从而造成了内存的浪费。 在C中出现内存泄露的主要原因就是程序猿在申请了内存后(malloc(), new)没有及时释放没用的内存空间甚至消灭了指针导致该区域内存空间根本无法释放。
知道了出现内存泄露的原因就能知道如何应对内存泄露即不用了的内存空间记得释放不释放留着过年哇
内存泄漏可能会导致严重的后果
程序运行后随着时间占用了更多的内存最后无内存可用而崩溃程序消耗了大量的内存导致其他程序无法正常使用程序消耗了大量内存导致消费者选用了别人的程序而不是你的经常做出内存泄露bug的程序猿被公司开出而贫困潦倒。
如何知道自己的程序存在内存泄露
根据内存泄露的原因及其恶劣的后果我们可以通过其主要表现来发现程序是否存在内存泄漏程序长时间运行后内存占用率一直不断的缓慢的上升而实际上在你的逻辑中并没有这么多的内存需求。
如何定位到泄露点呢
根据原理我们可以先review自己的代码利用查找功能查询new与delete看看内存的申请与释放是不是成对释放的这使你迅速发现一些逻辑较为简单的内存泄露情况。如果依旧发生内存泄露可以通过记录申请与释放的对象数目是否一致来判断。在类中追加一个静态变量 static int count;在构造函数中执行count;在析构函数中执行count--;通过在程序结束前将所有类析构之后输出静态变量看count的值是否为0如果为0,则问题并非出现在该处如果不为0则是该类型对象没有完全释放。检查类中申请的空间是否完全释放尤其是存在继承父类的情况看看子类中是否调用了父类的析构函数有可能会因为子类析构时没有是否父类中申请的内存空间。对于函数中申请的临时空间认真检查是否存在提前跳出函数的地方没有释放内存。 【如何优雅地检测内存泄漏5种内存泄漏的检测及定位工具重新理解内存】
C后台开发如何优雅地检测内存泄漏5种内存泄漏的检测及定位工具重新理解内存_哔哩哔哩_bilibiliwww.bilibili.com/video/BV1Fs4y177ht/?share_sourcecopy_webvd_source64f9e03b735a65ecab46f345348e1993编辑
STL 的智能指针
为了减少出现内存泄露的情况STL中使用智能指针来减少泄露。STL中一般有四种智能指针:
指针类别支持备注unique_ptrC 11拥有独有对象所有权语义的智能指针shared_ptrC 11拥有共享对象所有权语义的智能指针weak_ptrC 11到 std::shared_ptr 所管理对象的弱引用auto_ptrC 17中移除拥有严格对象所有权语义的智能指针 因为 auto_ptr 已经在 C 17 中移除对于面向未来的程序员来说最好减少在代码中出现该使用的频次吧这里我便不再研究该类型。又因为weak_ptr是shared_ptr的弱引用所以主要的只能指针分为两个unique_ptr和shared_ptr。
std::unique_ptr 是通过指针占有并管理另一对象并在 unique_ptr 离开作用域时释放该对象的智能指针。在下列两者之一发生时用关联的删除器释放对象
销毁了管理的 unique_ptr 对象通过 operator 或 reset() 赋值另一指针给管理的 unique_ptr 对象。
std::shared_ptr 是通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可占有同一对象。下列情况之一出现时销毁对象并解分配其内存
最后剩下的占有对象的 shared_ptr 被销毁最后剩下的占有对象的 shared_ptr 被通过 operator 或 reset() 赋值为另一指针。
unique_ptr
这是个独占式的指针对象在任何时间、资源只能被一个指针占有当unique_ptr离开作用域指针所包含的内容会被释放。
创建
unique_ptrint uptr( new int );
unique_ptrint[ ] uptr( new int[5] );
//声明,可以用一个指针显示的初始化,或者声明成一个空指针,可以指向一个类型为T的对象
shared_ptrT sp;
unique_ptrT up;
//赋值,返回相对应类型的智能指针,指向一个动态分配的T类型对象,并且用args来初始化这个对象
make_sharedT(args);
make_uniqueT(args); //注意make_unique是C14之后才有的
//用来做条件判断,如果其指向一个对象,则返回true否则返回false
p;
//解引用
*p;
//获得其保存的指针,一般不要用
p.get();
//交换指针
swap(p,q);
p.swap(q);
//release()用法//release()返回原来智能指针指向的指针,只负责转移控制权不负责释放内存常见的用法unique_ptrint q(p.release()) // 此时p失去了原来的的控制权交由q,同时p指向nullptr //所以如果单独用:p.release()//则会导致p丢了控制权的同时,原来的内存得不到释放//则会导致//reset()用法p.reset() // 释放p原来的对象,并将其置为nullptrp nullptr // 等同于上面一步p.reset(q) // 注意此处q为一个内置指针,令p释放原来的内存p新指向这个对象
类满足可移动构造 (MoveConstructible) 和可移动赋值 (MoveAssignable) 的要求但不满足可复制构造 (CopyConstructible) 或可复制赋值 (CopyAssignable) 的要求。 因此不可以使用 操作和拷贝构造函数仅能使用移动操作。
Demo
#include iostream
#include vector
#include memory
#include cstdio
#include fstream
#include cassert
#include functional
struct B {virtual void bar() { std::cout B::bar\n; }virtual ~B() default;
};
struct D : B
{D() { std::cout D::D\n; }~D() { std::cout D::~D\n; }void bar() override { std::cout D::bar\n; }
};
// 消费 unique_ptr 的函数能以值或以右值引用接收它
std::unique_ptrD pass_through(std::unique_ptrD p)
{p-bar();return p;
}
void close_file(std::FILE* fp) { std::fclose(fp); }
int main()
{std::cout unique ownership semantics demo\n;{auto p std::make_uniqueD(); // p 是占有 D 的 unique_ptrauto q pass_through(std::move(p));assert(!p); // 现在 p 不占有任何内容并保有空指针q-bar(); // 而 q 占有 D 对象} // ~D 调用于此
std::cout Runtime polymorphism demo\n;{std::unique_ptrB p std::make_uniqueD(); // p 是占有 D 的 unique_ptr// 作为指向基类的指针p-bar(); // 虚派发
std::vectorstd::unique_ptrB v; // unique_ptr 能存储于容器v.push_back(std::make_uniqueD());v.push_back(std::move(p));v.emplace_back(new D);for(auto p: v) p-bar(); // 虚派发} // ~D called 3 times
std::cout Custom deleter demo\n;std::ofstream(demo.txt) x; // 准备要读的文件{std::unique_ptrstd::FILE, void (*)(std::FILE*) fp(std::fopen(demo.txt, r),close_file);if(fp) // fopen 可以打开失败该情况下 fp 保有空指针std::cout (char)std::fgetc(fp.get()) \n;} // fclose() 调用于此但仅若 FILE* 不是空指针// 即 fopen 成功
std::cout Custom lambda-expression deleter demo\n;{std::unique_ptrD, std::functionvoid(D*) p(new D, [](D* ptr){std::cout destroying from a custom deleter...\n;delete ptr;}); // p 占有 Dp-bar();} // 调用上述 lambda 并销毁 D
std::cout Array form of unique_ptr demo\n;{std::unique_ptrD[] p{new D[3]};} // 调用 ~D 3 次
}输出结果
unique ownership semantics demo
D::D
D::bar
D::bar
D::~D
Runtime polymorphism demo
D::D
D::bar
D::D
D::D
D::bar
D::bar
D::bar
D::~D
D::~D
D::~D
Custom deleter demo
x
Custom lambda-expression deleter demo
D::D
D::bar
destroying from a custom deleter...
D::~D
Array form of unique_ptr demo
D::D
D::D
D::D
D::~D
D::~D
D::~D
shared_ptr
有两种方式创建 shared_ptr使用make_shared宏来加速创建的过程。因为shared_ptr主动分配内存并且保存引用计数(reference count),make_shared 以一种更有效率的方法来实现创建工作。
void main( )
{shared_ptrint sptr1( new int );shared_ptrint sptr2 make_sharedint(100);
}析构
shared_ptr默认调用delete释放关联的资源。如果用户采用一个不一样的析构策略时他可以自由指定构造这个shared_ptr的策略。在此场景下shared_ptr指向一组对象但是当离开作用域时默认的析构函数调用delete释放资源。实际上我们应该调用delete[]来销毁这个数组。用户可以通过调用一个函数例如一个lamda表达式来指定一个通用的释放步骤。
void main( )
{shared_ptrTest sptr1( new Test[5],[ ](Test* p) { delete[ ] p; } );
}注意 尽量不要用裸指针创建 shared_ptr以免出现分组不同导致错误
void main( )
{
// 错误int* p new int;shared_ptrint sptr1( p); // count 1shared_ptrint sptr2( p ); // count 1
// 正确shared_ptrint sptr1( new int ); // count 1shared_ptrint sptr2 sptr1; // count 2shared_ptrint sptr3; sptr3 sptr1 // count 3
}循环引用
因为 Shared_ptr 是多个指向的指针可能出现循环引用导致超出了作用域后仍有内存未能释放。
class B;
class A
{
public:A( ) : m_sptrB(nullptr) { };~A( ){cout A is destroyedendl;}shared_ptrB m_sptrB;
};
class B
{
public:B( ) : m_sptrA(nullptr) { };~B( ){cout B is destroyedendl;}shared_ptrA m_sptrA;
};
//***********************************************************
void main( )
{shared_ptrB sptrB( new B ); // sptB count 1shared_ptrA sptrA( new A ); // sptB count 1sptrB-m_sptrA sptrA; // sptB count 2sptrA-m_sptrB sptrB; // sptA count 2
}
// 超出定义域
// sptA count 1
// sptB count 2
demo
#include iostream
#include memory
#include thread
#include chrono
#include mutex
struct Base
{Base() { std::cout Base::Base()\n; }// 注意此处非虚析构函数 OK~Base() { std::cout Base::~Base()\n; }
};
struct Derived: public Base
{Derived() { std::cout Derived::Derived()\n; }~Derived() { std::cout Derived::~Derived()\n; }
};
void thr(std::shared_ptrBase p)
{std::this_thread::sleep_for(std::chrono::seconds(1));std::shared_ptrBase lp p; // 线程安全虽然自增共享的 use_count{static std::mutex io_mutex;std::lock_guardstd::mutex lk(io_mutex);std::cout local pointer in a thread:\n lp.get() lp.get() , lp.use_count() lp.use_count() \n;}
}
int main()
{std::shared_ptrBase p std::make_sharedDerived();
std::cout Created a shared Derived (as a pointer to Base)\n p.get() p.get() , p.use_count() p.use_count() \n;std::thread t1(thr, p), t2(thr, p), t3(thr, p);p.reset(); // 从 main 释放所有权std::cout Shared ownership between 3 threads and released\n ownership from main:\n p.get() p.get() , p.use_count() p.use_count() \n;t1.join(); t2.join(); t3.join();std::cout All threads completed, the last one deleted Derived\n;
}可能的输出结果
Base::Base()Derived::Derived()
Created a shared Derived (as a pointer to Base)p.get() 0xc99028, p.use_count() 1
Shared ownership between 3 threads and released
ownership from main:p.get() (nil), p.use_count() 0
local pointer in a thread:lp.get() 0xc99028, lp.use_count() 3
local pointer in a thread:lp.get() 0xc99028, lp.use_count() 4
local pointer in a thread:lp.get() 0xc99028, lp.use_count() 2Derived::~Derived()Base::~Base()
All threads completed, the last one deleted Derived
weak_ptr
std::weak_ptr 是一种智能指针它对被 std::shared_ptr 管理的对象存在非拥有性“弱”引用。在访问所引用的对象前必须先转换为 std::shared_ptr。
std::weak_ptr 用来表达临时所有权的概念当某个对象只有存在时才需要被访问而且随时可能被他人删除时可以使用 std::weak_ptr 来跟踪该对象。需要获得临时所有权时则将其转换为 std::shared_ptr此时如果原来的 std::shared_ptr 被销毁则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。
std::weak_ptr 的另一用法是打断 std::shared_ptr 所管理的对象组成的环状引用。若这种环被孤立例如无指向环中的外部共享指针则 shared_ptr 引用计数无法抵达零而内存被泄露。能令环中的指针之一为弱指针以避免此情况。
创建
void main( )
{shared_ptrTest sptr( new Test ); // 强引用 1weak_ptrTest wptr( sptr ); // 强引用 1 弱引用 1weak_ptrTest wptr1 wptr; // 强引用 1 弱引用 2
}将一个weak_ptr赋给另一个weak_ptr会增加弱引用计数(weak reference count)。 所以当shared_ptr离开作用域时其内的资源释放了这时候指向该shared_ptr的weak_ptr发生了什么weak_ptr过期了expired。如何判断weak_ptr是否指向有效资源有两种方法
调用use-count()去获取引用计数该方法只返回强引用计数并不返回弱引用计数。调用expired()方法。比调用use_count()方法速度更快。
从weak_ptr调用lock()可以得到shared_ptr或者直接将weak_ptr转型为shared_ptr
解决 shared_ptr 循环引用问题
class B;
class A
{
public:A( ) : m_a(5) { };~A( ){cout A is destroyedendl;}void PrintSpB( );weak_ptrB m_sptrB;int m_a;
};
class B
{
public:B( ) : m_b(10) { };~B( ){cout B is destroyedendl;}weak_ptrA m_sptrA;int m_b;
};
void A::PrintSpB( )
{if( !m_sptrB.expired() ){ cout m_sptrB.lock( )-m_bendl;}
}
void main( )
{shared_ptrB sptrB( new B );shared_ptrA sptrA( new A );sptrB-m_sptrA sptrA;sptrA-m_sptrB sptrB;sptrA-PrintSpB( );
}STL 智能指针的陷阱/不够智能的地方
尽量用make_shared/make_unique少用new std::shared_ptr在实现的时候使用的refcount技术因此内部会有一个计数器控制块用来管理数据和一个指针指向数据。因此在执行 std::shared_ptrA p2(new A) 的时候首先会申请数据的内存然后申请内控制块因此是两次内存申请而 std::make_sharedA()则是只执行一次内存申请将数据和控制块的申请放到一起。 不要使用相同的内置指针来初始化(或者reset)多个智能指针不要delete get()返回的指针不要用get()初始化/reset另一个智能指针智能指针管理的资源它只会默认删除new分配的内存,如果不是new分配的则要传递给其一个删除器不要把this指针交给智能指针管理 以下代码发生了什么事情呢还是同样的错误。把原生指针 this 同时交付给了 m_sp 和 p 管理这样会导致 this 指针被 delete 两次。 这里值得注意的是以上所说的交付给m_sp 和 p 管理不对并不是指不能多个shared_ptr同时占有同一类资源。shared_ptr之间的资源共享是通过shared_ptr智能指针拷贝、赋值实现的因为这样可以引起计数器的更新而如果直接通过原生指针来初始化就会导致m_sp和p都根本不知道对方的存在然而却两者都管理同一块地方。相当于”一间庙里请了两尊神”。 class Test{ public: void Do(){ m_sp shared_ptrTest(this); } private: shared_ptrTest m_sp; }; int main() { Test* t new Test; shared_ptrTest p(t); p-Do(); return 0; }不要把一个原生指针给多个shared_ptr或者unique_ptr管理
我们知道在使用原生指针对智能指针初始化的时候智能指针对象都视原生指针为自己管理的资源。换句话意思就说初始化多个智能指针之后这些智能指针都担负起释放内存的作用。那么就会导致该原生指针会被释放多次 int* ptr new int;
shared_ptrint p1(ptr);
shared_ptrint p2(ptr);
//p1,p2析构的时候都会释放ptr同一内存被释放多次不是使用new出来的空间要自定义删除器
以下代码试图将malloc产生的动态内存交给shared_ptr管理显然是有问题的delete 和 malloc 牛头不对马嘴 所以我们需要自定义删除器{ free(p); }传递给shared_ptr。 int main()
{int* pi (int*)malloc(4 * sizeof(int));shared_ptrint sp(pi);return 0;
}尽量不要使用 get()
智能指针设计者之处提供get()接口是为了使得智能指针也能够适配原生指针使用的相关函数。这个设计可以说是个好的设计也可以说是个失败的设计。因为根据封装的封闭原则我们将原生指针交付给智能指针管理我们就不应该也不能得到原生指针了因为原生指针唯一的管理者就应该是智能指针。而不是客户逻辑区的其他什么代码。 所以我们在使用get()的时候要额外小心禁止使用get()返回的原生指针再去初始化其他智能指针或者释放。(只能够被使用不能够被管理)。而下面这段代码就违反了这个规定 int main()
{shared_ptrint sp(new int(4));shared_ptrint pp(sp.get());return 0;
}
https://zhuanlan.zhihu.com/p/650440110