这是我参与「第三届青训营-后端场」笔记创作活动的的第1篇笔记
内存回收算法
引用计数算法
引用计数的原理为对于每一个对象,维护一个该对象的引用计数,当对象的引用计数为0时,可以通过gc回收对象的内存,但是该方法在并发时需要保证引用计数器的原子性,开销较大,并且存在着循环引用的问题。为了解决循环引用的问题,引入weak reference机制,类似于c++中的weak_ptr机制,weak_ptr指向一个shared_ptr对象,但并不会增加其引用计数,weak_ptr在不会对象造成任何影响,在对象未被释放时可以正常访问对象,释放后则不能访问对象。
作为一名c++程序员,c++语言不提供gc机制,在c++中内存分配主要分为三块:
- 全局变量、静态变量存储于静态存储区,其生命周期为整个程序运行时;
- 函数内部通过非new与malloc申请的局部变量,存在于栈上,生命周期为函数运行时;
- 通过new与malloc申请的变量,存在于堆上,需要通过程序调用delete与free手动释放,
但在c++11中引入了智能指针的数据结构,其能自己释放内存,不需要收到调用delete或free释放,具体实现如下:
#include "algorithm"
#include "iostream"
using namespace std;
template<typename T>
class shared_p{
private:
unsigned int *count;
T* ptr;
public:
shared_p(T* p):ptr(p), count(new unsigned int(1)){}
shared_p(const shared_p<T> &p):ptr(p.ptr), count(p.count){
(*count)++;
}
shared_p(const T *p):ptr(p), count(new unsigned int(1)){}
~shared_p(){
if(count != nullptr &&--(*count) == 0){
delete count;
delete ptr;
}
}
unsigned int getCount(){
return *(this->count);
}
shared_p<T>& operator= (const shared_p &p){
if(this == &p)
return *this;
if(count != nullptr &&--(*count) == 0){
delete count;
delete ptr;
}
(*p.count)++;
ptr = p.ptr;
count = p.count;
return *this;
}
T &operator*(){
return *(this->ptr);
}
T *operator->(){
return this->ptr;
}
};
int main(){
shared_p<string> p1(new string("hellow shared_ptr"));
cout<<p1.getCount()<<endl;
shared_p<string> p2(p1);
cout<<p1.getCount()<<" "<<p2.getCount()<<endl;
return 0;
}
追踪回收算法
追踪垃圾回收算法的机制类似于给内存的变量之间搭建一条路径,对于能够到达的路径则认为是不能回收的,对于不能到达的对象,则认为是需要回收的。 算法流程如下:
- 标记 静态变量、全局变量等作为路径的起始点
- 根据起始点,标记所有可到达点
- 回收不可到达点的内存
对于回收不可到达点内存则有三种算法:
- Copying GC,将存活的对象复制到另一块空间中
- Mark-sweep GC,将死亡对象的内存区域标记为可分配状态
- Mark-compact GC,将存活对象整理到内存中的起始区域,剩下区域做为可分配空间。
分代GC
之前没有听过的一种垃圾回收算法。
该算法假设大多数的对象都是在分配出来之后就很快不再使用。于是我们记录每个对象经历gc过程的次数,根据gc次数的不同,对对象分为不同的类别,存放于内存不同区域,根据不同类别选择不同的gc算法。
对于gc次数少的对象,那么下一次其内存不再使用的可能性较大,因此大部分的对象都会消亡,只是少数存活,因此可以使用Copying GC的方法,
但对于gc次数较多的对象,下一次内存不再使用的可能性较小,因此大部分对象下一次也能存活,则可以采用Mark-sweep GC方法。
内联函数
众所周知,函数内联可以提高程序的运行效率,具体体现在函数内联之后程序会少一步函数调用的过程,则可以避免一些堆栈操作,提高程序效率,但同时函数内联会提高代码的数目,膨胀代码。
并且函数的内联是由编译器所决定,对于函数内部逻辑过于复杂的函数体则不会内联。
在c++中,inline关键字修饰的函数为内联函数,同时,对于一个类在声明时就定义好函数的成员函数也是内联函数,但具体是否内联还是取决于编译器的优化。