内存溢出
out of memory:指程序在申请内存时,没有足够的内存空间供其使用(要求分配的内存超出了系统能给你的,系统不能满足需求)
c++:栈、堆
栈:1M
stack overflow
这个大小可以从编译器的属性中修改,但尽量不修改,不够用了又不得不用再去修改。
堆:4G
- C语言
void *malloc(size_t size);
申请一块大小为size的空间,需要用memset()初始化
void * realloc(void * ptr, size_t new_size);
用于扩大、缩小一个堆空间。ptr为null时,作用和malloc一样
当ptr不为空,设置原来分配的空间n,当new_size<n;缩小ptr所指向的内存空间,该内存块尾部的部分内存被拿掉。当new_size>n时,扩大*ptr所指向的内存空间,所有原来的空间尾部 有足够分配的空间,那么直接分配,没有则需要重新获取一块空间,拷贝原来的数据,释放原来的空间,并返回新的*ptr。
- C++
new 和delete 是C++ 定义的关键字,通过特定的语法可以组成表达式。
标准库函数实现,不是重载 new 和 delete 表达式,因为 new 和 delete 是不允许重载的。
//C++的库函数:
void *operator new(size_t); //allocate an object
void *operator delete(void *); //free an object
void *operator new[](size_t); //allocate an array
void *operator delete[](void *); //free an array
//C++ 在分配数组空间时多分配了 4 个字节的大小,专门保存数组的大小,在 delete [] 时取出这个保存的数就知道了需要调用析构函数多少次
/*
调用析构函数的次数是从数组对象指针前面的 4 个字节中取出;
传入 operator delete[] 函数的参数不是数组对象的指针 pAa,而是 pAa 的值减 4。
*/
实现过程
- pA=new A(10)
- 1、operator new 标准库函数,传入的参数为 class A 的大小,分配一个类A的内存
- 2、调用类的构造函数 对分配的内存进行初始化
- 3、返回新分配并构造好的对象的指针
- delete pA;
- 1、调用 pA 指向对象的析构函数
- 2、调用标准库函数 operator delete 来释放该对象的内存,传入函数的参数为 pA 的值
原因
- 长期保持某些资源的引用,垃圾回收器无法回收它,从而使该资源不能够及时释放 ---- 》 内存泄露
- 当需要保存多个耗用内存过大或当加载单个超大的对象时,该对象的大小超过了当前剩余的可用内存空间
eg:
- 全都是强引用(深拷贝)- 加载大量资源(图片、数据)
- 死循环(函数递归调用 死循环 一直在增加栈空间的使用)
- 出现大量的内存泄露
- 越界访问(c语言中) :strcpy函数需要有’\0’
解决
- 检查错误日志,查看 “ OutOfMemory ”错误前是否有其它异常或错误
- 查看代码:递归调用、大量的新对象生成、获取数据库数据的量
- 排查内存泄露问题
内存泄露
当使用 new/malloc 在堆上动态分配内存时,忘记使用 delete/free对内存进行回收,就出现了内存泄露。当大量的内存泄露出现时,就会影响服务器的性能和处理能力。
C++11:智能指针
解决内存泄露问题,使用智能指针,它会自动回收空间
- unique-ptr:独占型指针
- shard-ptr:共享型指针
- weak-ptr:解决shard-ptr中的死锁问题
linux
解决内存泄露
- top :查看当前系统运行的程序及其内存情况
通过虚拟内存的变化,如果运行时刻,内存一直在增加,该程序就出现了内存泄露
- mtrace : 用于查看内存泄露的位置
//在active.cpp文件
#include<mcheck.h>
···
//在程序开始处
mtrace();
//结束位置
muntrace();
//在终端
//第一种方式
//绑定,写到这个日志文件
expert MALLOC_TRACE = ./active.log
//运行
./active
//打开日志文件,找到内存泄露的地址[0x400626],使用addline显示位置
add2line -f -e active 0x400626
//第二种方式
//使用mtrace工具,查看active这个cpp文件,把日志写进active.log
mtrace active active.log
//显示文件,可以看到内存泄露的位置
cat active.log
- mtrace的实现原理
- hook 钩子
dlsym(RTLD_NEXT, “malloc”);
实现的原理是,mtrace里面有一个dlsym函数,捕抓malloc
防止内存泄露
内存池
1、内存如何分配 mp_malloc()
2、已分配的数据如何组织
内存池核心问题:内存块如何组织
伙伴算法:
先把需要的内存块分配,把剩下内存按照2^n 去分配,如过是相邻的2^k 就合并起来【缺点:回收条件太严谨】slab
选一个合适的n,把所有内存都分配为2n块,不管申请的大小,直接就给2n。【缺点:只需要1个字节,却分配了2^n个,多余太多】大小块
分为大块和小块,eg(32bytes) 分配的时候,通过需要内存的大小分配,每一个块里面,可能是提供了多次分配,只有当所有分配的内存使用完,完全释放后,这个块就会被回收。
3、内存如何释放 mp_free()
struct mp_pool_small
{
//当前使用的最后last
//这个块的最尾部end
//指向下一个块 next
unsigned char* last;
unsigned char* end;
mp_pool_small* next;
//加上一个失败,超过多少次分配失败 就不用继续分配它
int fails;
};
struct mp_pool_large
{
//中间不再细分
//指向下一个块 next
mp_pool_large* next;
void* alloc;
};
struct mp_pool_s
{
//指向小块,大块的地址
mp_pool_small* small;
mp_pool_large* large;
//指向当前的小块
mp_pool_small* current;
};