文档章节

c语言内存无法释放

liupengs
 liupengs
发布于 2013/06/15 14:15
字数 1686
阅读 534
收藏 6

       这两天用C语言做一个大数据处理的程序,对于一个处理大数据的程序来说除了考虑处理速度之处还有一个要考虑的就是内存管理(当然不是说操作系统的内存分配),因为数据量本身就非常大很多时候是没有额外的内存来让程序浪费的,所以我们要做的就是及时回收内存。

       但就在回收内存的时候我遇到一个问题:虽然我对每一个子串处理完了就将为它分配的内存释放,但是程序所占的内存还是越来越多,我首先考虑到是不是那里有分配 的空间忘了释放,检查一遍没有发现问题。如果这样那没有释放掉内存的原因可能就出现在释放内存的机制上了,我做了一个测试,代码如下:

Case #1:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct cut
{
	char a[1024*100];
	struct cut *next;


}cut;
typedef struct node
{
	char a[1024*100];
	struct node *next;
}node;
cut *cut_first,*cut_now;
node *node_first,*node_now;
int main()
{
	node *a;
	cut *b;
	int i;
	for(i=0;i<10;i++){
		a=(node *)malloc(sizeof(node));
		a->next=NULL;
		if(node_first==NULL) node_first=a;
		else node_now->next=a;
		node_now=a;

		b=(cut *)malloc(sizeof(cut));
		b->next=NULL;
		if(cut_first==NULL) cut_first=b;
		else cut_now->next=b;
		cut_now=b;

	}
    printf("malloc all \n");
	scanf("%d",&i);
	printf("start free node\n");
	while(node_first!=NULL)
	{
		node_now=node_first->next;
		free(node_first);
		node_first=node_now;
	}
	printf("have free node\n");
	scanf("%d",&i);
	printf("start free cut\n");
	while(cut_first!=NULL)
	{
		cut_now=cut_first->next;
		free(cut_first);
		cut_first=cut_now;
	}
	printf("have free cut\n");
	scanf("%d",&i);
	return 0;
}

      实验结果是内存直到程序结束才释放,free函数一点作用也没有发挥。因为我在结cut和node两个结构体分配内存时是交叉分配的(同时分配),我猜想问题就出在这是,因为同时分配导致分配给它们的内存空间也是相邻的,在释放的时候虽然将其中一个块结释放了但相邻的两块却没有释放,所以无法形成一个大的内存块(碎片过多)分配结其它需要内存的程序使用,所以这内存一直就被浪费,直到程序结束。(这只是我的一个猜想,我使用的是windows7    vc++6.0 我并不知道windows内存分配的算法,不过好像在linux也会出现这种问题,欢迎指正)。

    如果我不交叉分配,而是一次这一个结构体分配足够内存,释放内存的时候就能将空间释放掉。

Case #2:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct cut
{
	char a[1024*100];
	struct cut *next;


}cut;
typedef struct node
{
	char a[1024*100];
	struct node *next;
}node;
cut *cut_first,*cut_now;
node *node_first,*node_now;
int main()
{
	node *a;
	cut *b;
	int i;
	for(i=0;i<10;i++){
		a=(node *)malloc(sizeof(node));
		a->next=NULL;
		if(node_first==NULL) node_first=a;
		else node_now->next=a;
		node_now=a;
        }
	for(i=0;i<10;i++){
		b=(cut *)malloc(sizeof(cut));
		b->next=NULL;
		if(cut_first==NULL) cut_first=b;
		else cut_now->next=b;
		cut_now=b;
	}
	printf("malloc all \n");
	scanf("%d",&i);
	printf("start free node\n");
	while(node_first!=NULL)
	{
		node_now=node_first->next;
		free(node_first);
		node_first=node_now;
	}
	printf("have free node\n");
	scanf("%d",&i);
	printf("start free cut\n");
	i=0;
	while(cut_first!=NULL)
	{
		cut_now=cut_first->next;
		free(cut_first);
		cut_first=cut_now;
		i++;
	}
	printf("have free cut %d \n",i);
	scanf("%d",&i);
	return 0;
}

 

新更新:

    这篇博客是我写于好几年前, 最近正在研究glibc中内存管理方面的知识,顺便更新一下这篇博客。首先指出两种代码运行情况。

    1. 在windows 10 中 vs 2015 的环境下, 上面两种代码都不会出现内存无法释放(说明windows内存分配有变化)。

    2. 在 linux下 gcc version 5.4.0, 上面两种代码都会出现内存无法释放。

现在开始分析为什么在linux下内存无法释放(不对windows作讨论):

首先我们从代码Case #3入手:

Case #3:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct node
{
	char a[1024*100];
	struct node *next;
}node;

int main()
{
	node *node_first = NULL, *node_now;
	node *a;
	int i;
	char s_cmd[100];
	sprintf(s_cmd, "pmap -d %lu | grep mapped", getpid());
	//打印堆顶地址
	printf("before malloc, the top of heap is 0x%lx\n", sbrk(0));
	
	for(i=0;i<10;i++){
		a=(node *)malloc(sizeof(node));
		a->next=NULL;
		if(node_first==NULL) node_first=a;
		else node_now->next=a;
		node_now=a;
		//打印node节点的地址和堆顶地址
		printf("address of a is 0x%lx ", a);
		printf("top of heap after malloc is 0x%lx\n", sbrk(0));

	}
	a=(node *)malloc(sizeof(node)); //在这里分配一个节点是为了占用堆顶部,方便我们查看glibc内存管理方式
	printf("malloc end\n");
	system(s_cmd); //在这里用pmap 命令查看进程占用的内存
	printf("before free, the top of heap is 0x%lx\n", sbrk(0)); //打印node节点的地址和堆顶地址
	while(node_first!=NULL) //释放链表
	{
		node_now=node_first->next;
		free(node_first);
		node_first=node_now;
	}
	printf("after free, the top  of heap is 0x%lx\n", sbrk(0));
	system(s_cmd);//在这里用pmap 命令查看进程占用的内存, 链表释放后,程序占有的内存没变
	free(a);
	system(s_cmd);//在这里用pmap 命令查看进程占用的内存, 释放掉a后, 链表占有的内存才被释放掉。
	return 0;
}

代码一共分为两个部分: 首先我们动态申请一个链表, 然后释放这个链表, 并同时查看这个进程占用的内存。这段代码运行结果如下:

代码运行结果

从运行结果可以看出:

1.链表所有节点都是在堆上分配的(因为从运行结果可以看出链表各个节点的地址小于堆顶的地址)。

2.在链表释放前后进程占有的内存没有变化, 堆顶地址也没有变化(看第一个红框和第二个)。

3.在节点a释放后链表占用的内存归还给了操作系统(第三个红框显示,进程占用的内存有降低)。

为什么在释放掉链表node_first后进程占有的内存没有变化呢?

因为malloc采用了两中不同的方式来处理内存申请:

   1. 若分配内存小于 128k ,调用 sbrk() ,将堆顶指针向高地址移动,获得新的虚存空间。

   2. 若分配内存大于 128k ,调用 mmap() ,在文件映射区域中分配匿名虚存空间。

用mmap()分配的内存释放后会立即归还给系统, 然而采用sbrk()分配的内存调用free()后并不一定会马上规划给系统。具体细节请看参考文献【2】

因为链表每个节点的size小于128K,所以malloc在堆上为链表分配内存,于是在a释放前链表的占用内存没有被释放。 现在知道了为什么Case 1和2 在链表free后, 进程占有的内存不变的原因了。

参考:

【1】http://tencentdba.com/blog/linux-virtual-memory-glibc/

【2】http://bbs.csdn.net/topics/330179712

【3】http://blog.csdn.net/phenics/article/details/777053

 

© 著作权归作者所有

共有 人打赏支持
liupengs
粉丝 3
博文 5
码字总数 6999
作品 0
武汉
程序员
加载中

评论(1)

吟啸_徐行
吟啸_徐行
这都被你发现了
c与c++分别是怎样动态分配和释放内存的,有什么区别

C语言 c语言提供内存动态分配的函数有:malloc、calloc、realloc,在使用这些函数时必须包含其头文件,分别为: 1) malloc 函数: 在内存的动态分配区域中分配一个长度为size的连续空间,如果...

NineRec
2014/09/13
0
0
C语言内存管理讲解

谨记 人生有两条路,一天需要用心走,叫做梦想;一条需要用脚走,叫做现实。心走的太快,会迷路的;脚走的太快,会摔倒的;心走的太慢,现实会苍白;脚走的太慢,梦不会高飞。人生的精彩,是...

长风留言
2017/11/22
0
0
《从零开始学Swift》学习笔记(Day 71)——Swift与C/C++混合编程之数据类型映射

原创文章,欢迎转载。转载请注明:关东升的博客 如果引入必要的头文件,在Objective-C语言中可以使用C数据类型。而在Swift语言中是不能直接使用C数据类型,苹果公司为Swift语言提供与C语言相...

tony关东升
06/26
0
0
《从零开始学Swift》学习笔记(Day 71)——Swift与C/C++混合编程之数据类型映射

原创文章,欢迎转载。转载请注明:关东升的博客 如果引入必要的头文件,在Objective-C语言中可以使用C数据类型。而在Swift语言中是不能直接使用C数据类型,苹果公司为Swift语言提供与C语言相...

智捷课堂
2016/03/02
19
0
C语言的函数返回值

一:背景 谈到C语言的函数返回值,可能会感觉很亲切,不就是一个函数返回值嘛,当初学C语言的时候早就学过了很easy嘛,我曾经也是这么想的。后来要上研究生了,研究生阶段搞得就是C,所以又重...

Jeff_Linux
2014/07/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

49.Nginx防盗链 访问控制 解析php相关 代理服务器

12.13 Nginx防盗链 12.14 Nginx访问控制 12.15 Nginx解析php相关配置(502的问题) 12.16 Nginx代理 扩展 502问题汇总 http://ask.apelearn.com/question/9109 location优先级 http://blog....

王鑫linux
今天
1
0
Nginx防盗链、访问控制、解析php相关配置、Nginx代理

一、Nginx防盗链 1. 编辑虚拟主机配置文件 vim /usr/local/nginx/conf/vhost/test.com.conf 2. 在配置文件中添加如下的内容 { expires 7d; valid_referers none blocked server_names *.tes......

芬野de博客
今天
0
0
spring EL 和资源调用

资源调用 import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.PropertySource;import org.springframework.core.io.Resource;......

Canaan_
今天
1
0
memcached命令行、memcached数据导出和导入

一、memcached命令行 yum装telnet yum install telent 进入memcached telnet 127.0.0.1 11211 命令最后的2表示,两位字节,30表示过期时间(秒) 查看key1 get key1 删除:ctrl+删除键 二、m...

Zhouliang6
今天
1
0
Linux定时备份MySQL数据库

做项目有时候要备份数据库,手动备份太麻烦,所以找了一下定时备份数据库的方法 Linux里有一个 crontab 命令被用来提交和管理用户的需要周期性执行的任务,就像Windows里的定时任务一样,用这...

月夜中徘徊
今天
1
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部