内存相关问题

一,背景:

项目中经常遇到内存越界的问题,往往难以着手,同时浪费大的人力物力去解决内存越界问题。

二,现有知识:

2.1 内存越界的可能性

2.1.1 数组越界(包括List和vector)

2.1.2 内存操作函数越界(strcpy等)

2.1.3 字符串没有加 ‘\0’ 造成的越界。

2.1.4 多线程造成的越界(多为空指针)

例如grow函数如果是多线程,越界的可能性很大。

2.2 几种内存问题

2.2.2 内存泄漏(memory leak)
简单的new 没有delete
还有return之前没有delete
2.2.2 double delete(上溢出)
多发生于多线程处理,
或者把new出来的内存作为参数传递
2.2.3 数组下表访问越界(下溢出)
循环过程最后一个发生问题。
遍历过程删除某个节点也会发生问题。
2.2.4 栈溢出(多见于递归函数)
2.2.5 访问已经释放的内存(use after free)
野指针或者空指针
2.2.6 double delete
2.2.7 栈溢出(stack overflow)

三,工具和手段

3.1 工具 Valgrind

3.2 通过重载系统函数来hook住(一些特殊的case):

3.2.1 例如通过重载Vector和List,在使用下标访问前判断是否大于size来提示是否有越界

3.3 可以同重载new和delete等内存操作函数来监控内存问题

例如可以给每个使用者设置标签,
重载new,根据标签记录new的大小和次数,
重载delete,根据标签删除使用的内存的大小和次数
再根据内存变化来推测是否有内存泄漏。

3.4 _ASSERTE( _CrtCheckMemory( ) ); ​​​​

3.5 Kmemleak 内存泄漏检测工具

引用:https://blog.csdn.net/zhuyong006/article/details/83089407
要点:需要内核编译的时候打开选项

3.6 slub_debug

引用:https://www.cnblogs.com/arnoldlu/p/8568090.html

3.7 kasan

引用:https://www.cnblogs.com/arnoldlu/p/8568090.html

四,内存分配知识

4.1 几个内存分配的函数

1,内核空间:kmalloc
kmalloc() 申请的内存位于物理内存映射区域,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,因为存在较简单的转换关系,所以对申请的内存大小有限制,不能超过128KB。
2,内核空间:vmalloc
3,用户态:malloc
内存只有在要被 DMA 访问的时候才需要物理上连续,但为了性能上的考虑,内核中一般使用 kmalloc(),而只有在需要获得大块内存时才使用 vmalloc()。
4,三个区别和联系:
参考:https://www.cnblogs.com/Ph-one/p/4411423.html
4.1 vmalloc 中调用了 kmalloc
4.2 kmalloc分配内存是基于slab
备注:
DMA英文全称是Direct Memory Access,意思是直接存储器访问。

4.2 new和delete

4.2.1参考 https://www.cnblogs.com/raichen/p/5808766.html
参考:https://www.jianshu.com/p/a07ba8f384da
1,new运算符和operator new():
A* a = new A;我们知道这里分为两步:1.分配内存,2.调用A()构造对象。事实上,分配内存这一操作就是由operator new(size_t)来完成的,如果类A重载了operator new,那么将调用A::operator new(size_t ),如果没有重载,就调用::operator new(size_t ),全局new操作符由C++默认提供。因此前面的两步也就是:1.调用operator new 2.调用构造函数。
(1)new :不能被重载,其行为总是一致的。它先调用operator new分配内存,然后调用构造函数初始化那段内存。

new 操作符的执行过程:

  1. 调用operator new分配内存 ;
  2. 调用构造函数生成类对象;
  3. 返回相应指针。

(2)operator new:要实现不同的内存分配行为,应该重载operator new,而不是new。

operator new就像operator + 一样,是可以重载的。如果类中没有重载operator new,那么调用的就是全局的::operator new来完成堆的分配。同理,operator new[]、operator delete、operator delete[]也是可以重载的。
2,这里我们可以这么理解,new表达式(new operator)其实可以分解为两部,即先调用new操作符(operator new)申请内存,再调用placement new来初始化对象。即上面对new表达式的使用string * str = new string(“hello”);等价于下面两句:

void *buffer = ::operator new(sizeof(string));
buffer = new(buffer) string(“hello”);
void* operator new(size_t sizt)
{
      return malloc(size);      
}

4.3 C++当中3种new的用法

参考:https://www.jianshu.com/p/a07ba8f384da
1, new operator/delete operator
new operator,也叫new表达式,是我们在cpp中最常见的new的用法,比如:
string *pStr = new string(“abd”);
这个new表达式完成了两件事情:申请内存和调用类的构造函数初始化对象。

与之相对应的是delete operator(delete 表达式),它完成了两件事情:调用所指向元素的析构函数,然后释放内存空间。

2,operator new(operator new[])/operator delete(operator delete[])
operator new(或者operator new[]),其实是标准库函数,容易和new operator搞混。类似与c当中的malloc库函数,只负责申请一块原始的未命名的内存空间,例如:
void *buffer = ::operator new(sizeof(string));

3, placement new
placement new,也叫定位new表达式,它用于在给定的内存中初始化对象(但不分配内存),例如(第二行,第一行是operator new,合起来就是 new operator):

void *buffer = ::operator new(sizeof(string));
buffer = new(buffer) string(“abd”);

九,参考

9.1 内存泄漏专题:

https://www.cnblogs.com/arnoldlu/p/8051674.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值