内存基本概念
内存溢出(out of memory):是指程序在申请内存时,没有足够的内存空间供其使用。内存申请函数有new,malloc,memalign等;
内存泄露(memory leak):向系统申请分配内存时使用(new),但是使用完后不归还(delete),使得申请到的那块内存你自己也不能再访问了(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。
以发生的方式来分类,内存泄漏可以分为4 类:
-
常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
-
偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
-
一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
-
隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存,泄漏它更难被检测到。
注意:memory leak 发生多了最终会导致out of memory。
内存越界:是指向系统申请一块内存后,使用时却超出申请范围。比如一些操作内存的函数:sprintf、strcpy、strcat、vsprintf、memcpy、memset、memmove。当造成内存泄漏的代码运行时,所带来的错误是无法避免的,通常会造成
1.破坏了堆中内存内存分配信息数据
2.破坏了程序其他对象的内存空间
3.破坏了空闲内存块
例如:
string str = “this is a test string!!!”;
char buf[16] = {0};
strcpy(buf, str.c_str()); //out of buffer space 将str字符串中到’\0’结束前的25个字符(含空格哦)复制到只能容下16个字符的buf数组中,结果导致内存越界
另外,在大型项目中,如果一个结构体发生变化,并且该结构体使用在多个库当中,那么这几个库都需要重新编译,否则就会导致内存越界。
注意:内存越界跟内存溢出的区别,前者是在使用系统提供的内存时,做了一些超出申请的内存范围的操作;而后者则是在申请内存大小时就已超出系统能提供的。
排查的原则,首先是保证能重现错误,根据错误估计可能的环节,逐步裁减代码,缩小排查空间。
检查所有的内存操作函数,检查内存越界的可能。常用的内存操作函数:
sprintf snprintf
vsprintf vsnprintf
strcpy strncpy strcat
memcpy memmove memset bcopy
如果有用到自己编写的动态库的情况,要确保动态库的编译与程序编译的环境一致。
缓冲区溢出:程序为了临时存取数据的需要,一般会分配一些内存空间称为缓冲区。如果向缓冲区中写入缓冲区无法容纳的数据,就会造成缓冲区以外的存储单元被改写,称为缓冲区溢出。
注意:缓冲区溢出和内存溢出的区别,前者是溢出后的数据会覆盖到计算机内存中以前的内容。除非这些被覆盖的内容被保存或能够恢复,否则就会永远丢失。黑客入侵的一种就是用精心编写的入侵代码(一种恶意程序)使缓冲区溢出,然后用自己预设的方法处理缓冲区,并且执行,从而达到入侵操纵。
栈溢出:是缓冲区溢出的一种,原理也是相同的。分为上溢出和下溢出。其中,上溢出是指栈满而又向其增加新的数据,导致数据溢出,数据溢出使得有用的存储单元被改写,往往会引发不可预料的后果;下溢出是指空栈而又进行删除操作等,导致空间溢出。
踩内存:是访问了不应该访问的内存地址。尤其在C指针中,可以访问不合法的内存。
例如:
1.访问越界数组
int a[10];
int *p = a;
int c = p[11];
//这时变量指针p指向的是一个非法内存。已经越界了。这是越界数组访问导致的踩内存。
2.访问已经被free释放掉的内存
char *a = (char *)malloc(sizeof(char) * 10);//申请内存
char *p = a;
char c ;
free(a);
c = p[1];
//这时,指针访问已经被释放的内存块a。这是访问已经被free掉的内存导致的踩内存。
3.栈内存访问越界
int b = 1;
int a[20] = {0};
int c ;
int *p = a;
c = p[20];
//首先变量b入栈,然后申请数组a[20]入栈,p[20]已经超出了数组的合法范围,
//这时访问的是栈中变量b的内存。访问了不合法的内存。
参考资料:
https://blog.csdn.net/u011318165/article/details/47806245
https://blog.csdn.net/u012209626/article/details/47112347
http://blog.51cto.com/qiaopeng688/2091842
上一篇: 【TCP协议】(1)---TCP协议详解