内存释放失败问题
- 最近在MCU上使用内存去存储图片数据时出现内存释放失败的问题,导致内存资源一直在占用,后面定位发现了问题,故记录一下。
1.现象及原因
- 现象:为存储拍得的图像数据,使用malloc在堆内申请了两块内存空间。一块用于存储源图片数据,一块用于保存二维化后的灰度图片数据,在使用结束后发现申请的内存释放失败,导致后面程序申请不到新的内存。
使用的大致流程如下:
unsigned char *yuv_data = NULL;
unsigned char *binar_y_data = NULL;
char ret = 0;
yuv_data = (unsigned char *)malloc(sizeof(unsigned char)640 * 480 * 3);
if(yuv_data == NULL) {
printf("yuv_data malloc fail");
return -1;
}
binar_y_data = (unsigned char *)malloc(sizeof(unsigned char)640 * 480);
if(binar_y_data == NULL) {
printf("binar_y_data malloc fail");
return -1;
}
//获取图片数据,存于内存中,格式为YUV444
ret = get_camera_data(yuv_data);
if(ret != 0) {
printf("get camera data fail");
return -1;
}
//得到图片数据提取Y值,然后二值化处理,并将二值化后的数据打印
ret = binary_data(binar_y_data,yuv_data,(640 * 480 * 3));
if(ret != 0) {
printf("get binary data fail");
return -1;
}
printf("printf binary data start\n\r");
for(int i = 0 ; i < (640*480) ;i++) {
printf("0x%02x ",*(binar_y_data++));
}
printf("printf binary data end\n\r");
free(binar_y_data);//该处报错
free(yuv_data);
在释放binar_y_data这块内存时,打印报出释放失败,后定位,发现由于遗漏没有在打印二值化数据之前备份这块内存的首地址,导致内存释放失败
解决方法是:将malloc得到的内存首地址保存起来,释放时传入得到的这块内存首地址即可
unsigned char *bak_addr = NULL;
bak_addr = binar_y_data;
printf("printf binary data start\n\r");
for(int i = 0 ; i < (640*480) ;i++) {
printf("0x%02x ",*(binar_y_data++));
}
printf("printf binary data end\n\r");
binar_y_data = bak_addr;
free(binar_y_data);//该处报错
free(yuv_data);
这时内存就可以正常释放了,原因就是疏忽了malloc和free的用法,使用free函数时,传参需是在malloc时获取的空间地址,至于为什么这样后面会说到;可能很多朋友也是只知道使用malloc和free,没有关注实现的细节,故而踩坑。
2.malloc和free
具体实现详情可见网友的这篇文章malloc和free源代码实现。
- 在申请内存时实际分配的空间分为空间头和空间体,空间体才是我们真正用来存储数据的地方,空间头是记录空间体的相关信息,包含当前这块内存的已用空间、剩余空间、指向下一块内存的指针等信息,用于管理内存的。
- 实际使用malloc时返回的是空间体的首地址,而在释放内存时,可以看见free函数中会根据传入的地址,回退找到空间头,将该空间体的内存属性改为可用,从而实现释放操作
void free(void* ap)
{
Header *bp,*p,*prev;
bp=(Header*)ap-1;
for(prev=memptr,p=memptr->s.next;
(p!=bp) && (p!=memptr);prev=p,p=p->next);
if(p!=bp) return;
prev->s.freesize+=p->s.usedsize+p->s.freesize;
prev->s.next=p->s.next;
memptr=prev;
}