文档章节

c语言内存无法释放

liupengs
 liupengs
发布于 2013/06/15 14:15
字数 1686
阅读 533
收藏 6
点赞 0
评论 1

       这两天用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

C语言内存管理讲解

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

长风留言 ⋅ 2017/11/22 ⋅ 0

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

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

tony关东升 ⋅ 2016/03/09 ⋅ 0

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

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

智捷课堂 ⋅ 2016/03/02 ⋅ 0

Objective-C高级编程之内存管理篇

iOS的内存管理是采用引用计数的方式,引用计数分为手动引用计数和自动引用计数(ARC)。前者要求开发者手动管理内存,自己负责内存的申请与释放,后者是苹果推出的自动管理内存的方式,但其实...

凝望向北 ⋅ 2017/02/24 ⋅ 0

C语言的函数返回值

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

Jeff_Linux ⋅ 2014/07/26 ⋅ 0

C语言开发中的内存检测基本模块

一、前言 在用C语言开发产品时,由于C语言中的指针操作的灵活性,因此在使用指针时也需要特别小心,稍微不严谨就会导致内存问题,如内存未初始化,内存泄露,内存重复释放等。 但是在实际开发...

墙头草 ⋅ 2011/05/26 ⋅ 0

阿里面试题整理集合

电面一面(规定控制在一个小时左右)】 1、malloc和new的区别: (1)malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都是程序运行中申请动态内存和释放内存的。 (2)...

SibylY ⋅ 2013/08/09 ⋅ 0

Go和C如何共享内存资源

Go语言作为一个现代化的编程语言以及支持垃圾内存的自动回收特性(GC).我们现在关注的是C语言返回的内存资源的自动回收技术. CGO初步 Go语言的cgo技术允许在Go代码中方便的使用C语言代码. 基本...

chai2010 ⋅ 2013/10/13 ⋅ 2

[从C到C++] 1.9 C++ new和delete操作符

在C语言中,动态分配和释放内存的函数是malloc、calloc和free,而在C++语言中,new、new[]、delete和delete[]操作符通常会被用来动态地分配内存和释放内存。 需要注意的是new、new[]、delet...

wu_being ⋅ 前天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Java Web如何操作Cookie的添加修改和删除

创建Cookie对象 Cookie cookie = new Cookie("id", "1"); 修改Cookie值 cookie.setValue("2"); 设置Cookie有效期和删除Cookie cookie.setMaxAge(24*60*60); // Cookie有效时间 co......

二营长意大利炮 ⋅ 今天 ⋅ 0

【每天一个JQuery特效】淡入淡出显示或隐藏窗口

我是JQuery新手爱好者,有时间就练练代码,防止手生,争取每天一个JQuery练习,在这个博客记录下学习的笔记。 本特效主要采用fadeIn()和fadeOut()方法显示淡入淡出的显示效果显示或隐藏元...

Rhymo-Wu ⋅ 今天 ⋅ 0

Spring JDBC使用方法

普通实现: 1、创建数据表customer。 可以使用任何数据库实现,在项目中要引入相应数据库驱动包并配置相应数据库连接。 2、创建Customer pojo。 Customer类的属性对应数据库的属性,除了为每...

霍淇滨 ⋅ 今天 ⋅ 0

Contos 7 安装Jenkins

Jenkins是一款能提高效率的软件,它能帮你把软件开发过程形成工作流,典型的工作流包括以下几个步骤 开发 提交 编译 测试 发布 有了Jenkins的帮助,在这5步中,除了第1步,后续的4步都是自动...

欧虞山 ⋅ 今天 ⋅ 0

revel

revel install go get github.com/revel/revelgo get github.com/revel/cmd create new app revel new git.oschina.net/zdglf/myapp run app revel run git.oschina.net/zdglf/myapp ot......

zdglf ⋅ 今天 ⋅ 0

49. Group Anagrams - LeetCode

Question 49. Group Anagrams Solution 思路:维护一个map,key是输入数组中的字符串(根据字符排好序) Java实现: public List<List<String>> groupAnagrams(String[] strs) { Map<Strin......

yysue ⋅ 今天 ⋅ 0

spring Email

使用spring发Email其实就是使用spring自己封装携带的一个javamail.JavaMailSenderImpl类而已。这个类可以当一个普通的java对象来使用,也可以通过把它配置变成spring Bean的方式然后注入使用...

BobwithB ⋅ 今天 ⋅ 0

spark 整理的一些知识

Spark 知识点 请描述spark RDD原理与特征? RDD全称是resilient distributed dataset(具有弹性的分布式数据集)。一个RDD仅仅是一个分布式的元素集合。在Spark中,所有工作都表示为创建新的...

tuoleisi77 ⋅ 今天 ⋅ 0

思考

时间一天天过感觉自己有在成长吗?最怕的是时光匆匆而过,自己没有收获!下面总结下最近自己的思考。 认识自己 认识另一个自己,人们常说要虚心听取别人意见和建议。然而人往往是很难做到的,...

hello_hp ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部