为什么C++没有python 内存回收机制制?

爆笑Gif动画,看懂C++和Java的垃圾回收机制
· 2&年前 |
阅读(37880)·
评论()| 来源
对C++的手动内存释放和Java的自动垃圾回收没概念?那就看看下面两个动画吧。
C++的手动垃圾回收Java的自动垃圾回收
来源: - 即时通讯开发者社区!
即时通讯框架hot
开源移动端即时通讯框架。
轻量级Web端即时通讯框架。
移动端实时音视频框架。
基于MobileIMSDK的IM系统。
本月热门资讯
12345678910
技术好文分类
最新社区主题
123456789101112131415
商务/合作:
投稿/报道:
手机访问本站
微信公众号new浅议C++ 中的垃圾回收方法_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
浅议C++ 中的垃圾回收方法
阅读已结束,下载文档到电脑
想免费下载更多文档?
定制HR最喜欢的简历
你可能喜欢C++内存管理机制
我的图书馆
C++内存管理机制
在C++中,内存可分为系统数据区,自由存储区,文本区,const数据区,全局静态区,堆区和栈区。堆存储区的行为类似于静态存储区,当我们在堆上分配内存之后,如果不进行手动的释放,其内存是不会自动释放掉的。但是在JAVA中,有一种叫做垃圾清理的机制可以自动清理堆内存,但是在C++中没有这样的机制。也就是说,在C++中,如果我们分配了堆内存,就必须手动释放它。否则如果我们不停的分配堆内存,但是不对其进行释放,当对内存被耗尽是就会造成程序崩溃。一般地,用new分配的变量是存放于堆内存中的,但是返回的指针变量是存放在栈中的。当我们在一个子函数中new了一个变量,但是在函数返回时既没有保存new返回的指针,也没有delete时,就会造成内存泄露。如果我们写的是服务器程序,不断地内存泄露所造成的最终结果就是服务器死机。但是在windows、linux以及其他一些成熟的系统中,都有类似于内存保护的机制。系统会给用户程序分配一定的运行所需的内存,同是也会给系统自身的运行保留一部分内存,这部分内存是用户程序所不能访问的。如果我们编写的程序存在内存泄露,当耗尽系统给应用程序分配的内存之后,程序就会停止运行,而不会造成系统的司机。&至于栈内存,也是我们在写程序中用到的最多的情况。程序中定义的每一个临时对象,new所返回的指针,以及递归函数中变量都是存放在栈中的。栈内存是可以自动释放的,当我们在某个模块中定义了一个对象,在该模块结束时,变量所占据的内存就会被系统回收,在定义新的变量时,新的变量就有可能存放在原变量所在的地址上,但是在系统回收栈内存的时候,是不会清空所释放的栈内存中的数据的,只是将栈顶重新调整,并在新数据的到来时将其分配到栈顶。&详细出处参考:http://www.jb51.net/article/31879.htm 11:38:30 zhangjianyong
TA的最新馆藏[转]&[转]&[转]&[转]&
喜欢该文的人也喜欢博客访问: 376051
博文数量: 77
博客积分: 2621
博客等级: 少校
技术积分: 1484
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
C与C++的内存机制虽然非常相似,但还是有一些不同的地方。下面让我们一起来看看它们到底有什么区别吧。(一)C内存机制1. 栈(Stack):& & & & 位于函数内的局部变量(包括函数实参),由编译器负责分配释放,函数结束,栈变量失效。2. 堆(Heap) :& & & & 由程序员用malloc()/calloc()/realloc()分配空间,free()释放所申请的空间。如果程序员忘记free(),则会造成内存泄漏,程序结束时可能会由操作系统回收,也许就一直占用着直至关机。3. 全局区/静态区(Global Static Area):&& & & & 全局变量和静态变量存放区,程序一经编译好,该区域便存在。并且在C语言中初始化的全局变量和静态变量和未初始化的放在相邻的两个区域(在C++中,由于编译器会给全局变量和静态变量自动初始化赋值,所以没有区分了)。由于全局变量一直占据内存空间且不易维护,推荐少用。程序结束时释放。&4. C风格字符串常量存储区:& & & & 专门存放字符串常量的地方,程序结束时释放。5. 程序代码区:& & & & 存放程序二进制代码的区域。&(二)C++内存机制1. 栈(Stack):&& & & & 位于函数内的局部变量(包括函数实参),由编译器负责分配释放,函数结束,栈变量失效。2. 堆(Heap):&& & & & 这里与C不同的是,该堆是由new申请的内存,由delete负责释放。3. 自由存储区(Free Storage):&& & & & 由程序员用malloc()/calloc()/realloc()分配空间,由free()释放。如果程序员忘记free()了,则会造成内存泄漏,程序结束时可能会有操作系统回收,也许就一直占用着直至关机。& & & & 与C的堆机制对应。4. 全局区/静态区(Global Static Area):&& & & & &全局变量和静态变量存放区,程序一经编译好,该区域便存在。在C++中,由于编译器会给全局变量和静态变量自动初始化赋值,所以没有区分初始化和未初始化变量。由于全局变量一直占据内存空间且不易维护,推荐少用。程序结束时释放。5. 常量存储区:& & & & & 这是一块比较特殊的存储区,专门存储不能修改的常量(如果采用非正常手段更改,当然也是可以的)。举个例子来说明一下C和C++在全局区/静态区的区别:(1)C程序:#include <stdio.h>static int a;int b;int c = 7;intmain(void){&&&&return 0;}将.c源文件编译链接,生成可执行文件,打印一下文件的大小如下:可以看到,数据段(不包括bss)大小为256,bss(未初始化数据段)大小为16。接下来,我们对b进行初始化,代码如下:#include <stdio.h>static int a;int b = 9;int c = 7;intmain(void){&&&&return 0;}可执行文件空间分配如下:可以看到,数据段(不包括bss)大小为260,bss(未初始化数据段)大小为12。& & & &与上面的那段代码对比可以发现,data增加了4,刚刚好是bss减少的4。现在,就可以确定C语言中,对全局区/静态区中变量初始化与为初始化是放在不同区域的。特别注意:我在编译的时候增加了一个选项-std=c89。为什么要增加这个选项呢?最开始我编译上面的程序没有加-std=c89这个选项,以gcc 1.c进行文件编译,无论变量b是未初始化还是初始化了,size a.out打印出来的结果都没有变化。而让人奇怪的是,一模一样的程序在郑康的机子上却打印出有变化的结果,这让我们感觉很奇怪。我们都gcc -v查看了自己的gcc版本,发现都是一样的4.6.3。那是为什么呢?后来发现我装了g++,而郑康的没有装,我就在想会不会是这个原因呢?可是后来发现还是不是的。后来突然想起来,以前有一个程序,我的电脑支持动态数组,而郑康的不支持。因为它的是c89标准,而我的是c99标准,c99中增加了很多C++的特性在里面。于是我在编译的时候增加了一个选项-std=c89,果然文件大小分配出现了变化。所以,如果各位的机子上出现了和我一样的结果,那很有可能是标准问题哦!说了这么多,我们来看看c++程序的运行结果吧。b未初始化:b初始化后:这次我们看得很清楚哦,没有变化的。再说一个我感觉很奇怪的现象:#include <stdio.h>static int a;int b = 0;int c = 7;intmain(void){&&&&return 0;}这个程序与之前b未初始化的b相比,将b初始化为0。gcc 1.c -std=c89后,结果如下:这个结果是不是和你想得不太一样呢?为什么初始化了的变量b,bss大小还是16,而不是像将b初始化为9那样大小为12?我们猜测可能是编译器默认将全局变量/静态变量(整形)初始化为0,所以我们将其初始化为0是没有达到真正的“初始化”效果的。
阅读(2858) | 评论(0) | 转发(10) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。关于C++为什么不加入垃圾回收机制解析
投稿:jingxian
字体:[ ] 类型:转载 时间:
C++为什么不加入垃圾回收机制呢?现在肯定还有很多人不太了解,不过没关系,下面小编就为大家详细的介绍下究竟C++为什么不加入垃圾回收机制。一起跟随小编过来看看吧
Java的爱好者们经常批评C++中没有提供与Java类似的垃圾回收(Gabage Collector)机制(这很正常,正如C++的爱好者有时也攻击Java没有这个没有那个,或者这个不行那个不够好),导致C++中对动态存储的官吏称为程序员的噩梦,不是吗?你经常听到的是内存遗失(memory leak)和非法指针存取,这一定令你很头疼,而且你又不能抛弃指针带来的灵活性。
在本文中,我并不想揭露Java提供的垃圾回收机制的天生缺陷,而是指出了C++中引入垃圾回收的可行性。请读者注意,这里介绍的方法更多的是基于当前标准和库设计的角度,而不是要求修改语言定义或者扩展编译器。
什么是垃圾回收?
作为支持指针的编程语言,C++将动态管理存储器资源的便利性交给了程序员。在使用指针形式的对象时(请注意,由于引用在初始化后不能更改引用目标的语言机制的限制,多态性应用大多数情况下依赖于指针进行),程序员必须自己完成存储器的分配、使用和释放,语言本身在此过程中不能提供任何帮助,也许除了按照你的要求正确的和操作系统亲密合作,完成实际的存储器管理。标准文本中,多次提到了“未定义(undefined)”,而这大多数情况下和指针相关。
某些语言提供了垃圾回收机制,也就是说程序员仅负责分配存储器和使用,而由语言本身负责释放不再使用的存储器,这样程序员就从讨厌的存储器管理的工作中脱身了。然而C++并没有提供类似的机制,C++的设计者Bjarne Stroustrup在我所知的唯一一本介绍语言设计的思想和哲学的著作《The Design and Evolution of C++》(中译本:C++语言的设计和演化)中花了一个小节讨论这个特性。简而言之,Bjarne本人认为,
“我有意这样设计C++,使它不依赖于自动垃圾回收(通常就直接说垃圾回收)。这是基于自己对垃圾回收系统的经验,我很害怕那种严重的空间和时间开销,也害怕由于实现和移植垃圾回收系统而带来的复杂性。还有,垃圾回收将使C++不适合做许多底层的工作,而这却正是它的一个设计目标。但我喜欢垃圾回收的思想,它是一种机制,能够简化设计、排除掉许多产生错误的根源。
需要垃圾回收的基本理由是很容易理解的:用户的使用方便以及比用户提供的存储管理模式更可靠。而反对垃圾回收的理由也有很多,但都不是最根本的,而是关于实现和效率方面的。
已经有充分多的论据可以反驳:每个应用在有了垃圾回收之后会做的更好些。类似的,也有充分的论据可以反对:没有应用可能因为有了垃圾回收而做得更好。
并不是每个程序都需要永远无休止的运行下去;并不是所有的代码都是基础性的库代码;对于许多应用而言,出现一点存储流失是可以接受的;许多应用可以管理自己的存储,而不需要垃圾回收或者其他与之相关的技术,如引用计数等。
我的结论是,从原则上和可行性上说,垃圾回收都是需要的。但是对今天的用户以及普遍的使用和硬件而言,我们还无法承受将C++的语义和它的基本库定义在垃圾回收系统之上的负担。”
以我之见,统一的自动垃圾回收系统无法适用于各种不同的应用环境,而又不至于导致实现上的负担。稍后我将设计一个针对特定类型的可选的垃圾回收器,可以很明显地看到,或多或少总是存在一些效率上的开销,如果强迫C++用户必须接受这一点,也许是不可取的。
关于为什么C++没有垃圾回收以及可能的在C++中为此做出的努力,上面提到的著作是我所看过的对这个问题叙述的最全面的,尽管只有短短的一个小节的内容,但是已经涵盖了很多内容,这正是Bjarne著作的一贯特点,言简意赅而内韵十足。
下面一步一步地向大家介绍我自己土制佳酿的垃圾回收系统,可以按照需要自由选用,而不影响其他代码。
构造函数和析构函数
C++中提供的构造函数和析构函数很好的解决了自动释放资源的需求。Bjarne有一句名言,“资源需求就是初始化(Resource Inquirment Is Initialization)”。
因此,我们可以将需要分配的资源在构造函数中申请完成,而在析构函数中释放已经分配的资源,只要对象的生存期结束,对象请求分配的资源即被自动释放。
那么就仅剩下一个问题了,如果对象本身是在自由存储区(Free Store,也就是所谓的“堆”)中动态创建的,并由指针管理(相信你已经知道为什么了),则还是必须通过编码显式的调用析构函数,当然是借助指针的delete表达式。
幸运的是,出于某些原因,C++的标准库中至少引入了一种类型的智能指针,虽然在使用上有局限性,但是它刚好可以解决我们的这个难题,这就是标准库中唯一的一个智能指针::std::auto_ptr&&。
它将指针包装成了类,并且重载了反引用(dereference)运算符operator *和成员选择运算符operator -&,以模仿指针的行为。关于auto_ptr&&的具体细节,参阅《The C++ Standard Library》(中译本:C++标准库)。
例如以下代码,
#include & cstring &
#include & memory &
#include & iostream &
class string
string(const char* cstr) { _data=new char [ strlen(cstr)+1 ]; strcpy(_data, cstr); }
~string() { delete [] _ }
const char* c_str() const { return _ }
void foo()
::std::auto_ptr & string & str ( new string( " hello " ) );
::std::cout && str-&c_str() && ::std::
由于str是函数的局部对象,因此在函数退出点生存期结束,此时auto_ptr&string&的析构函数调用,自动销毁内部指针维护的string对象(先前在构造函数中通过new表达式分配而来的),并进而执行string的析构函数,释放为实际的字符串动态申请的内存。在string中也可能管理其他类型的资源,如用于多线程环境下的同步资源。下图说明了上面的过程。
进入函数foo 退出函数
auto_ptr&string&::auto&string&() auto_ptr&string&::~auto_ptr&string&()
string::string() string::~string()
_data=new char[] delete [] _data
使用资源 -----------------------------------& 释放资源
现在我们拥有了最简单的垃圾回收机制(我隐瞒了一点,在string中,你仍然需要自己编码控制对象的动态创建和销毁,但是这种情况下的准则极其简单,就是在构造函数中分配资源,在析构函数中释放资源,就好像飞机驾驶员必须在起飞后和降落前检查起落架一样。),即使在foo函数中发生了异常,str的生存期也会结束,C++保证自然退出时发生的一切在异常发生时一样会有效。
auto_ptr&&只是智能指针的一种,它的复制行为提供了所有权转移的语义,即智能指针在复制时将对内部维护的实际指针的所有权进行了转移,例如
auto_ptr & string & str1( new string( & str1 & ) );
cout && str1-&c_str();
auto_ptr & string & str2(str1); // str1内部指针不再指向原来的对象
cout && str2-&c_str();
cout && str1-&c_str(); // 未定义,str1内部指针不再有效
某些时候,需要共享同一个对象,此时auto_ptr就不敷使用,由于某些历史的原因,C++的标准库中并没有提供其他形式的智能指针,走投无路了吗?
另一种智能指针
但是我们可以自己制作另一种形式的智能指针,也就是具有值复制语义的,并且共享值的智能指针。
需要同一个类的多个对象同时拥有一个对象的拷贝时,我们可以使用引用计数(Reference Counting/Using Counting)来实现,曾经这是一个C++中为了提高效率与COW(copy on write,改写时复制)技术一起被广泛使用的技术,后来证明在多线程应用中,COW为了保证行为的正确反而导致了效率降低(Herb Shutter的在C++ Report杂志中的Guru专栏以及整理后出版的《More Exceptional C++》中专门讨论了这个问题)。
然而对于我们目前的问题,引用计数本身并不会有太大的问题,因为没有牵涉到复制问题,为了保证多线程环境下的正确,并不需要过多的效率牺牲,但是为了简化问题,这里忽略了对于多线程安全的考虑。
首先我们仿造auto_ptr设计了一个类模板(出自Herb Shutter的《More Execptional C++》),
template & typename T &
class shared_ptr
class implement // 实现类,引用计数
implement(T* pp):p(pp),refs(1){}
~implement(){}
T* // 实际指针
size_ // 引用计数
implement* _
explicit shared_ptr(T* p)
: _impl(new implement(p)){}
~shared_ptr()
decrease(); // 计数递减
shared_ptr(const shared_ptr& rhs)
: _impl(rhs._impl)
increase(); // 计数递增
shared_ptr& operator=(const shared_ptr& rhs)
if (_impl != rhs._impl) // 避免自赋值
decrease(); // 计数递减,不再共享原对象
_impl=rhs._ // 共享新的对象
increase(); // 计数递增,维护正确的引用计数值
T* operator-&() const
return _impl-&p;
T& operator*() const
return *(_impl-&p);
void decrease()
if (--(_impl-&refs)==0)
{ // 不再被共享,销毁对象
void increase()
++(_impl-&refs);
这个类模板是如此的简单,所以都不需要对代码进行太多地说明。这里仅仅给出一个简单的使用实例,足以说明shared_ptr&&作为简单的垃圾回收器的替代品。
void foo1(shared_ptr & int && val)
shared_ptr & int & temp(val);
*temp=300;
void foo2(shared_ptr & int && val)
val=shared_ptr & int & ( new int(200) );
int main()
shared_ptr & int & val(new int(100));
cout&&"val="&&*
foo1(val);
cout&&"val="&&*
foo2(val);
cout&&"val="&&*
在main()函数中,先调用foo1(val),函数中使用了一个局部对象temp,它和val共享同一份数据,并修改了实际值,函数返回后,val拥有的值同样也发生了变化,而实际上val本身并没有修改过。
然后调用了foo2(val),函数中使用了一个无名的临时对象创建了一个新值,使用赋值表达式修改了val,同时val和临时对象拥有同一个值,函数返回时,val仍然拥有这正确的值。
最后,在整个过程中,除了在使用shared_ptr & int &的构造函数时使用了new表达式创建新之外,并没有任何删除指针的动作,但是所有的内存管理均正确无误,这就是得益于shared_ptr&&的精巧的设计。
拥有了auto_ptr&&和shared_ptr&&两大利器以后,应该足以应付大多数情况下的垃圾回收了,如果你需要更复杂语义(主要是指复制时的语义)的智能指针,可以参考boost的源代码,其中设计了多种类型的智能指针。
对于需要在程序中拥有相同类型的多个对象,善用标准库提供的各种容器类,可以最大限度的杜绝显式的内存管理,然而标准容器并不适用于储存指针,这样对于多态性的支持仍然面临困境。
使用智能指针作为容器的元素类型,然而标准容器和算法大多数需要值复制语义的元素,前面介绍的转移所有权的auto_ptr和自制的共享对象的shared_ptr都不能提供正确的值复制语义,Herb Sutter在《More Execptional C++》中设计了一个具有完全复制语义的智能指针ValuePtr,解决了指针用于标准容器的问题。
然而,多态性仍然没有解决,我将在另一篇文章专门介绍使用容器管理多态对象的问题。
为什么不在C++语言中增加对垃圾回收的支持?
根据前面的讨论,我们可以看见,不同的应用环境,也许需要不同的垃圾回收器,不管三七二十一使用垃圾回收,需要将这些不同类型的垃圾回收器整合在一起,即使可以成功(对此我感到怀疑),也会导致效率成本的增加。
这违反了C++的设计哲学,“不为不必要的功能支付代价”,强迫用户接受垃圾回收的代价并不可取。
相反,按需选择你自己需要的垃圾回收器,需要掌握的规则与显式的管理内存相比,简单的多,也不容易出错。
最关键的一点, C++并不是“傻瓜型”的编程语言,他青睐喜欢和善于思考的编程者,设计一个合适自己需要的垃圾回收器,正是对喜爱C++的程序员的一种挑战。
以上就是小编为大家带来的关于C++为什么不加入垃圾回收机制解析全部内容了,希望大家多多支持脚本之家~
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具

我要回帖

更多关于 java内存回收机制 的文章

 

随机推荐