分享

内存报错:corruption,Cannot allocate memory

 海漩涡 2016-05-11

前段时间开发的一个后端C模块上线后,线上出core,初始时,因为訪问压力不大,所以崩溃是上线3天左右出现的。当时用gdb跟进调用堆栈并检查源代码,发现出core位置的代码沒有啥问题。因为当时开发任务较重,且该模块不保存状态(崩溃重新启动不影响对外服务),所以沒有深入跟进。后来随着client版本号逐渐放量导致訪问压力上升,噩梦開始了。。。
该模块会不定时core掉,并且差点儿每次崩溃时的调用堆栈都不一样,关键是最后几层堆栈非常多都位于差点儿不可能出问题的代码中,比方库函数或厂里的公共库。
好在在众多core文件里发现规律:每次基本都是在对内存动态操作时挂掉,比方malloc/realloc/free/new/delete都引起了崩溃。并且幸运的是,崩溃进程还是输出了一些关键信息,比方以下这些(这些是在不同的崩溃时刻分别输出的):
*** glibc detected *** malloc(): memory corruption: 0x0000002a95c1ff10 ***
*** glibc detected *** double free or corruption (out): 0x0000000000f0d910 ***
*** glibc detected *** free(): invalid next size (normal): 0x0000002a96103b00 ***
*** glibc detected *** free(): invalid next size (fast): 0x0000000000f349d0 ***
*** glibc detected *** corrupted double-linked list: 0x0000002a95f062e0 ***
从上面的日志也能够看到,每次引起崩溃的直接原因都可能不同。用gdb又细致查看core文件发现,有时进程是收到SIGABRT信号后退出,有时又是收到SIGSEGV信号后退出。
由此,基本定位了崩溃原因:内存訪问越界导致破坏了heap的数据结构。用valgrind在线下环境启动进程,试图重现崩溃或定位越界訪问的代码,遗憾的是,脚本压了1个小时也没出现崩溃,而valgrind的输出报告也沒有越界代码位置的提示。
终于,细致检查源代码后发现,在某个回调函数中,new出来的buffer接收完通过http post方式发送过来的2进制数据后,我又多写了1行代码,相似于:recv_buf[data_len] = '\0',导致越界多写1个字节,终于引起各种莫名其妙的内存崩溃。
经验教训:
1)调用堆栈信息对定位问题帮助非常大,但也不可尽信。比方这次遇到的情况,每次出core的调用堆栈差点儿都不一样,并且最后几层栈帧都是不可能出现故障的库函数或久经考验的公司公共库,这样的情况下,思维须要跳出局部,在更高的层次寻找规律或原因
2)一旦定位崩溃属于堆内存读写越界问题,就细致检查自己的代码吧,因为库函数或公共库出问题的概率太小了,所以不要存在侥幸心理,这个时候,盲目的自信要不得
3)本来自觉得对内存操作已经非常小心了,没想到还是在想当然的瞬间写下犯错的代码,导致终于花费非常多时间和精力去“捉虫”。只是好在跟进崩溃的过程中添加了一点分析/定位问题的经验,也算有些收获吧

=============== EOF =================




====================================================
1、
一次申请超过剩余内存大小,会“Cannot allocate memory”

但是多次申请内存,总量大于内存总大小却没有报错?????


2、一次申请的内存,在不使用的情况下,free、top查看内存情况无变化


3、将内存memset清零时,free、top查看内存减少申请的大小



================================================================


昨天在修改自己的代码的时候,碰到了malloc函数内存分配失败,上网翻了翻,一个很可能的原因是之前的代码出现了越界操作,导致malloc分配函数所涉及的一些信息被破坏。在这个思想的指导下,今天又是郁闷了一整天,来来回回看自己的代码。又加不断的调试,终于发现自己的代码中有一个malloc分配的内存大小为0,不是自己预想的大小,而之后的代码又按预想的大小对内存进行了操作,导致了下一个malloc无法分配内存。
总结自己的问题,如果下一次再碰到这样的问题,就要查从不能分配的那个malloc函数开始往回找最近的那个能分配的malloc,出问题的代码应该就在这部分,很可能的原因就是指针越界,对未知的内存进行了操作,导致了malloc不能继续分配内存。


=================================================================


以前分配的比这还大不代表你这次分配大内存就能成功。分配成功的前提是你进程内部有足够大的连续可用的内存
注意:前提有两个,第一是可用,第二还必须“连续”
也就是说,要是你进程内没有一块内存大于你要求的尺寸,分配就失败


=============================================================

在C语言中, 执行到malloc程序core的时候, 一般人的第一反应是内存空间不足. 常见的代码为:

  1. #define SIZE 100  
  2. int* p = (int*)malloc(SIZE * sizeof(int));  
但是, 事实情况是: 内存还多的很.

有的C编译器对没有声明的函数, 是不报错的(有的连警告都没有, C编译器认为程序员永远是正确的). 糟糕的是, C编译器默认认为函数的返回值是int类型. 如果没有加以下头文件的话:

  1. #include <stdlib.h>  

程序把malloc的返回值强转成int, 然后再转成int*. 于是程序core了.



==============================================================


最近在代码调试过程中发现一很无解的bug,跟踪到代码发现malloc()函数总是申请空间失败,返回0地址。这类bug往往很难定位,因为前面运行正常不出错。经过仔细琢磨发现问题出现在malloc()函数前面的代码中,如果前面代码使用到memset或者memcpy等类似函数,并且在使用过程中发生地址越界,则可能导致后面的malloc申请空间失败,所以此时应检测前面的代码。

别人已经提到的类似例子:

#include <stdlib.h>
#include 
<stdio.h>
#include 
<memory.h>

int main()
{
    #define BUF_SIZE 25

    _asm 
int 3;
    
char* lpszBuf = (char*)malloc(BUF_SIZE);
    memset(lpszBuf, 
0x00, BUF_SIZE*2);
    
char* lpszTest = (char*)malloc(BUF_SIZE); // <==== 内存分配失败
    free(lpszBuf);                            // <==== 出错 执行到 int 3; 指令
    free(lpszTest);

    
return 0;
}


==============================================================


malloc失败情况

1、堆空间被越界的操作或整理操作破坏了
memset、*指针赋值

2、内存碎片多,没有一块连续的内存适合申请的大小,即使内存剩余总量大于需要申请的大小也会失败。


3、在C语言中, 执行到malloc程序core的时候, 一般人的第一反应是内存空间不足. 常见的代码为:

  1. #define SIZE 100  
  2. int* p = (int*)malloc(SIZE * sizeof(int));  
但是, 事实情况是: 内存还多的很.

有的C编译器对没有声明的函数, 是不报错的(有的连警告都没有, C编译器认为程序员永远是正确的). 糟糕的是, C编译器默认认为函数的返回值是int类型. 如果没有加以下头文件的话:

  1. #include <stdlib.h>  

程序把malloc的返回值强转成int, 然后再转成int*. 于是程序core了.



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多