文档章节

c语言内存无法释放

liupengs
 liupengs
发布于 2013/06/15 14:15
字数 1686
阅读 544
收藏 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

没有更多内容

加载失败,请刷新页面

加载更多

区块链教程以太坊源码分析chain-indexer区块链索引一

兄弟连区块链教程以太坊源码分析chain-indexer区块链索引一 chain_indexer 区块链索引 chain_indexer.go 源码解析 chain_indexer 顾名思义, 就是用来给区块链创建索引的功能。 之前在eth协议...

兄弟连区块链入门教程
32分钟前
2
0
社会化分享插件集成分享

一.前提摘要 社会化分享每个app必备的推广需求,无论是拉新,邀请,游戏奖励,等等都离不开分享的影子,下面我们介绍下社会化分享的插件; 首先要说下,现在的社交App,社区App等,国内外的...

佳妮
33分钟前
4
0
IOC 之 Spring 统一资源加载策略

统一资源:Resource org.springframework.core.io.Resource 为 Spring 框架所有资源的抽象和访问接口 它继承 org.springframework.core.io.InputStreamSource接口 作为所有资源的统一抽象,S...

职业搬砖20年
33分钟前
3
0
Python爬虫实战入门一:工具准备

一、基础知识 使用Python编写爬虫,当然至少得了解Python基本的语法,了解: 基本数据结构 数据类型 控制流 函数的使用 模块的使用 不需要过多过深的Python知识,仅此而已。 个人推荐《Pytho...

糖宝lsh
37分钟前
2
0
Hanlp中使用纯JAVA实现CRF分词

Hanlp中使用纯JAVA实现CRF分词 与基于隐马尔可夫模型的最短路径分词、N-最短路径分词相比,基于条件随机场(CRF)的分词对未登录词有更好的支持。本文(HanLP)使用纯Java实现CRF模型的读取与...

左手的倒影
39分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部