奇怪的 内存 释放问题。delete 之后,内存无法释放

J_流儿 2018-02-02 06:44:02
#include <iostream>
#include <map>
using namespace std;

int main(int argc, char* argv[])
{
int i = 0;
std::cout << "waitting for input, you can check current memory" << std::endl;
std::cin >> i;
char** ptr = new char *[1000000];
std::map<int, char *> tMap;

for (unsigned long i = 0; i < 1000000; i ++)
{
ptr[i] = new char[3000];
tMap.insert(make_pair(i, ptr[i]));
}
std::cout << "waitting for input, you can check current memory" << std::endl;
std::cin >> i;
for (unsigned long i = 0; i < 1000000; i ++)
{
delete []ptr[i];
}
delete []ptr;
std::cout << "waitting for input, you can check current memory" << std::endl;
std::cin >> i;
return 0;
}


上面代码,运行到 26行的时候,内存不会释放,一直显示进程占用内存 2.9G。
但是如果 删除 第 16行,delete 之后,内存就恢复到 几十M了。

我尝试 MALLOC_MMAP_MAX_=1000000 MALLOC_MMAP_THRESHOLD_=1024 ./memtest
命令来运行 , 结果还是一样的。在 第16行存在的时候,内存一直无法释放。
...全文
985 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
CrackWei 2021-05-23
  • 打赏
  • 举报
回复
J_流儿的回答是正确的。这种情况是因为内存并没有交还给系统,需要手动交还。 具体可以搜索SetProcessWorkingSetSize和EmptyWorkingSet这两个函数。 但是调用这两个函数只是“假“的交还。 目前没有更好的办法。
斯幽柏雷科技 2021-02-16
  • 打赏
  • 举报
回复
楼主解决了吗,我在win10下同样代码没问题,换linux也这样。。
J_流儿 2018-02-03
  • 打赏
  • 举报
回复
引用 12 楼 super_admi 的回复:
我用VS 2005/2008进一步测试发现: 如果是调试状态下,3G左右分配的内存,delete之后,并没有释放的迹象,而最后程序结束了,似乎才慢慢释放内存; 如果直接运行编译好的exe程序(DEBUG版本和RELEASE版本都试过),则3G内存被delete之后,内存占用明显快速减少,最后大约只有100多M的占用,考虑到map还没释放,应该是可以理解的。 所以,我不清楚楼主是不是在调试状态下查看内存的,我这里明显就是调试器的问题。
不清楚是不是和调试器有关系,我后面没有在Windows 下继续测试了。上面的那个解释应该八九不离十了。准备去看下源码再说了
super_admi 2018-02-03
  • 打赏
  • 举报
回复
我用VS 2005/2008进一步测试发现: 如果是调试状态下,3G左右分配的内存,delete之后,并没有释放的迹象,而最后程序结束了,似乎才慢慢释放内存; 如果直接运行编译好的exe程序(DEBUG版本和RELEASE版本都试过),则3G内存被delete之后,内存占用明显快速减少,最后大约只有100多M的占用,考虑到map还没释放,应该是可以理解的。 所以,我不清楚楼主是不是在调试状态下查看内存的,我这里明显就是调试器的问题。
J_流儿 2018-02-03
  • 打赏
  • 举报
回复
这个问题我自己现在能够解释到的是: 1、内存并没有泄露,只是没有归还给系统。如果在这段程序 的后面 继续分配内存的话,进程的heap 是不会继续增加的。 2、中间的tMap 申请的内存破坏了 空闲内存的连续性,导致 进程一直没有办法 凑够一块完整的内存(在ptmalloc2上,堆顶的连续空闲内存到达64k以上,才会主动归还内存给OS,不然就是自己管理。在不同的编译器上效果可能不一样。) 3、更详细的细节,关于怎么分配的,如何影响的还没有一个结果,内存管理太复杂了。
J_流儿 2018-02-03
  • 打赏
  • 举报
回复
引用 7 楼 super_admi 的回复:
我这里测试情况如下: 1.WIN10+GCC 32位:内存爆掉; 2.减少一个数量级,WIN10+GCC 32位,分配正常,释放正常; 3.WIN7 + VS 2005 64位:分配正常,释放时卡死。内存分配完成,应用程序大约占了3G多,而不是楼主说的2.9G。
不同的编译器内存分配方式不一样吧,2.9G 是在 Cent OS 6.8上看到的结果。
super_admi 2018-02-03
  • 打赏
  • 举报
回复
楼上搞笑吧?你以为是在栈上分配的内存?还是有自动内存管理?
cutmelon 2018-02-03
  • 打赏
  • 举报
回复
这个不存在内存泄漏问题啊,简单来说就是作用域问题,有临时变量没有释放而已,贴两段代码自己体会一下吧,注意注释掉的花括号
#include <iostream>
#include <map>
using namespace std;

int main(int argc,char* argv[])
{
	int i = 0;
	std::cout<<"waitting for input, you can check current memory"<<std::endl;
	std::cin>>i;
//	{
		char** ptr = new char *[1000000];
		std::map<int,char *> tMap;

		for (unsigned long i = 0; i < 1000000; i++)
		{
			ptr[i] = new char[3000];
			{
				tMap.insert(make_pair(i,ptr[i]));
			}
		}
		std::cout<<"waitting for input, you can check current memory"<<std::endl;
		std::cin>>i;
		for (unsigned long i = 0; i < 1000000; i++)
		{
			delete[]ptr[i];
		}
		delete[]ptr;
//	}
	std::cout<<"waitting for input, you can check current memory"<<std::endl;
	std::cin>>i;
	return 0;
}
#include <iostream>
#include <map>
using namespace std;

int main(int argc,char* argv[])
{
	int i = 0;
	std::cout<<"waitting for input, you can check current memory"<<std::endl;
	std::cin>>i;
	{
		char** ptr = new char *[1000000];
		std::map<int,char *> tMap;

		for (unsigned long i = 0; i < 1000000; i++)
		{
			ptr[i] = new char[3000];
//			{
				tMap.insert(make_pair(i,ptr[i]));
//			}
		}
		std::cout<<"waitting for input, you can check current memory"<<std::endl;
		std::cin>>i;
		for (unsigned long i = 0; i < 1000000; i++)
		{
			delete[]ptr[i];
		}
		delete[]ptr;
	}
	std::cout<<"waitting for input, you can check current memory"<<std::endl;
	std::cin>>i;
	return 0;
}
一般在函数中使用按楼主原来写法没什么问题,如果要严格控制内存,参考上面的代码
super_admi 2018-02-03
  • 打赏
  • 举报
回复
我这里测试情况如下: 1.WIN10+GCC 32位:内存爆掉; 2.减少一个数量级,WIN10+GCC 32位,分配正常,释放正常; 3.WIN7 + VS 2005 64位:分配正常,释放时卡死。内存分配完成,应用程序大约占了3G多,而不是楼主说的2.9G。
paschen 版主 2018-02-03
  • 打赏
  • 举报
回复
一些系统在一些情况下对释放的内存可能并不立即被系统收回,而只是进行相应的标记,在程序下一次申请时可直接使用,从而提高效率,但在系统需要内存且内存不足时这部分内存才被系统回收
J_流儿 2018-02-02
  • 打赏
  • 举报
回复
引用 5 楼 paschen 的回复:
在WIN10测试没有发现不会释放,注释不注释那句都会减少到70M
不同的编译器好像效果不一样,clang 在某些情况下会出现,Windows 只在 VisualStudio2010 上 出现过。 gcc 一直都会有,不过我现在改了代码之后,可以释放一半儿,剩下一半儿还是无法释放。貌似知道什么原因,但是还解释不了。还在继续研究
paschen 版主 2018-02-02
  • 打赏
  • 举报
回复
在WIN10测试没有发现不会释放,注释不注释那句都会减少到70M
J_流儿 2018-02-02
  • 打赏
  • 举报
回复
引用 3 楼 zjq9931 的回复:
试验了一下,确实如此。 不过在map的作用域结束的时候,内存会慢慢释放掉。
有发现过这种现象,只是没有搞清楚这两者之间相互影响的细节
  • 打赏
  • 举报
回复
试验了一下,确实如此。 不过在map的作用域结束的时候,内存会慢慢释放掉。
J_流儿 2018-02-02
  • 打赏
  • 举报
回复
引用 1 楼 super_admi 的回复:
分配和释放内存,减少一个数量级试试。
目前内存分配不大,总共也才 2.9G。 试了下。 减少一个数量级也没用
super_admi 2018-02-02
  • 打赏
  • 举报
回复
分配和释放内存,减少一个数量级试试。
前 言 6 第1章 文件结构 11 1.1 版权和版本的声明 11 1.2 头文件的结构 12 1.3 定义文件的结构 13 1.4 头文件的作用 13 1.5 目录结构 14 第2章 程序的版式 15 2.1 空行 15 2.2 代码行 16 2.3 代码行内的空格 17 2.4 对齐 18 2.5 长行拆分 19 2.6 修饰符的位置 19 2.7 注释 20 2.8 类的版式 21 第3章 命名规则 22 3.1 共性规则 22 3.2 简单的WINDOWS应用程序命名规则 23 3.3 简单的UNIX应用程序命名规则 25 第4章 表达式和基本语句 26 4.1 运算符的优先级 26 4.2 复合表达式 27 4.3 IF 语句 27 4.4 循环语句的效率 29 4.5 FOR 语句的循环控制变量 30 4.6 SWITCH语句 30 4.7 GOTO语句 31 第5章 常量 33 5.1 为什么需要常量 33 5.2 CONST 与 #DEFINE的比较 33 5.3 常量定义规则 33 5.4 类中的常量 34 第6章 函数设计 36 6.1 参数的规则 36 6.2 返回值的规则 37 6.3 函数内部实现的规则 39 6.4 其它建议 40 6.5 使用断言 41 6.6 引用与指针的比较 42 第7章 内存管理 44 7.1内存分配方式 44 7.2常见的内存错误及其对策 44 7.3指针与数组的对比 45 7.4指针参数是如何传递内存的? 47 7.5 FREE和DELETE把指针怎么啦? 50 7.6 动态内存会被自动释放吗? 50 7.7 杜绝“野指针” 51 7.8 有了MALLOC/FREE为什么还要NEW/DELETE ? 52 7.9 内存耗尽怎么办? 53 7.10 MALLOC/FREE 的使用要点 54 7.11 NEW/DELETE 的使用要点 55 7.12 一些心得体会 56 第8章 C++函数的高级特性 57 8.1 函数重载的概念 57 8.2 成员函数的重载、覆盖与隐藏 60 8.3 参数的缺省值 63 8.4 运算符重载 64 8.5 函数内联 65 8.6 一些心得体会 68 第9章 类的构造函数、析构函数与赋值函数 69 9.1 构造函数与析构函数的起源 69 9.2 构造函数的初始化表 70 9.3 构造和析构的次序 72 9.4 示例:类STRING的构造函数与析构函数 72 9.5 不要轻视拷贝构造函数与赋值函数 73 9.6 示例:类STRING的拷贝构造函数与赋值函数 73 9.7 偷懒的办法处理拷贝构造函数与赋值函数 75 9.8 如何在派生类中实现类的基本函数 75 9.9 一些心得体会 77 第10章 类的继承与组合 78 10.1 继承 78 10.2 组合 80 第11章 其它编程经验 82 11.1 使用CONST提高函数的健壮性 82 11.2 提高程序的效率 84 11.3 一些有益的建议 85 参考文献 87 附录A :C++/C代码审查表 88 附录B :C++/C试题 93 附录C :C++/C试题的答案与评分标准 97 前 言 软件质量是被大多数程序员挂在嘴上而不是放在心上的东西! 除了完全外行和真正的编程高手外,初读本书,你最先的感受将是惊慌:“哇!我以前捏造的C++/C程序怎么会有那么多的毛病?” 别难过,作者只不过比你早几年、多几次惊慌而已。 请花一两个小时认真阅读这本百页经书,你将会获益匪浅,这是前面N-1个读者的建议。 一、编程老手与高手的误区 自从计算机问世以来,程序设计就成了令人羡慕的职业,程序员在受人宠爱之后容易发展成为毛病特多却常能自我臭美的群体。 如今在Internet上流传的“真正”的程序员据说是这样的: (1) 真正的程序员没有进度表,只有讨好领导的马屁精才有进度表,真正的程序员会让领导提心吊胆。 (2) 真正的程序员不写使用说明书,用户应当自己去猜想程序的功能。 (3) 真正的程序员几乎不写代码的注释,如果注释很难写,它理所当然也很难读。 (4) 真正的程序员不画流程图,原始人和文盲才会干这事。 (5) 真正的程序员不看参考手册,新手和胆小鬼才会看。 (6) 真正的程序员不写文档也不需要文档,只有看不懂程序的笨蛋才用文档。 (7) 真正的程序员认为自己比用户更明白用户需要什么。 (8) 真正的程序员不接受团队开发的理念,除非他自己是头头。 (9) 真正的程序员的程序不会在第一次就正确运行,但是他们愿意守着机器进行若干个30小时的调试改错。 (10) 真正的程序员不会在上午9:00到下午5:00之间工作,如果你看到他在上午9:00工作,这表明他从昨晚一直干到现在。 …… 具备上述特征越多,越显得水平高,资格老。所以别奇怪,程序员的很多缺点竟然可以被当作优点来欣赏。就象在武侠小说中,那些独来独往、不受约束且带点邪气的高手最令人崇拜。我曾经也这样信奉,并且希望自己成为那样的“真正”的程序员,结果没有得到好下场。

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧