看到DreamHead的精彩文章《对象的生命》,也想对此谈一谈自己的观点。
对象的状态就像任何其它生命体一样,其LifeCycle为:
创建(降生)-->存在(活着)--->销毁(死亡)
这是任何一个对象都存在的大状态机(State Machine),对于不同的对象类别,我们可以为其创建各自不同的状态机,这些状态机中的所有状态其实都是"存在(活着)"状态的子状态. 也就是说,当我们为一个类设计状态机时,其实是在为"存在(活着)"状态进行分解.
Bjarne Stroustrup在创建C++时,对C++的定位是"General Programming Language",这就意味着他想让C++可以用于任何领域,可以解决任何问题.所以它必须兼顾功能和性能.当一个功能的引入会带来性能上的大量损耗时,C++宁愿放弃其所带来的便利.GC的性能问题无疑会让C++望而却步.
另外,由于其"通用编程语言"的定位,C++程序的执行环境千差万别,这就意味着如果C++支持GC,它必须针对每一个执行环境进行GC的实现,这些不可能完全靠编译器完成,而且要靠库的支持.这会给C++程序的移植带来困难.
或许你会说,由于C++还存在其他相对于C而言对性能带来损耗的特性,比如virtual,而C++对此的哲学是:"如果你不用它,则你无需为其付费",那么为什么C++不提供可选择的GC特性,也就是说,如果程序员愿意使用GC,则说明他愿意忍受GC的性能问题(还有其他的问题),否则,它可以不使用它.
That's a good question!!! 但由于一个大规模的C++程序可以使用来自于不同提供商提供的库,而这些库有些可能会使用GC,而另外一些不使用,让程序员再使用这些库时,既要考虑GC的情况,又要考虑非GC的情况,这很容易让程序员陷入混乱,从而造成更多的内存问题.
C++对GC问题的解决方案是,它提供相关的支持,如果程序员想实现GC,它可以重载全局的或基于class的operator new,然后自己来设计GC机制,自己来实现或使用相关的GC库. 这种解决方案带来的便利是,由于GC的算法有很多种,各自存在着优缺点,程序员可以根据自己的需要选择合适的算法.
正是因为C++语言本身不支持GC,所以它提供了构造/析构机制,事实上,从面向对象的语义上来说,只有提供了构造/析构机制,其面向对象语义才是完整的.这不是Bjarne Stroustrup的发明,因为这种机制之前就存在,Bjarne Stroustrup只不过在C++中继承了它们.
一个对象的创建过程分为2步:
分配对象需要的内存 ---> 调用其构造函数.
第一步平淡无奇,因为内存是对象真正存在的载体,第2步才能够让对象构造的语义变得完整.因为在第一步我们只是得到了一块Raw Memory,这块Memory如果没有被构造,本身根本就不是一个对象.只有实施了构造过程之后,这块内存才变得有意义.所以,即使一个class本身没有提供构造函数,C++仍然会自动产生一个默认的构造函数来构造它.
而对象的销毁过程则稍有不同,它也分为两步:
调用其析构函数(如果存在的话) ---> 释放对象占用内存.
析构函数提供了一种机制,程序员可以指定一个对象所占用的内存被释放之前,某些操作可以被自动执行.这些操作主要包括对象本身占用的资源(除了自身占用的内存).这种机制为程序员提供了极大的便利,它的意义从某种程度上而言超过了GC,因为程序员除了在析购函数内管理内存之外,还可以管理其他资源,GC机制则无法做到这一点.
另外,与构造函数不同的是,没有默认的析构函数。如果一个class以及其super class没有析构函数,则对象的销毁过程只是释放内存而已。
我们在来谈谈Java,Java语言的定位不是"通用编程语言",虽然它也可以在大多数领域大显身手,但它绝对无法涉足某些领域.它被创建的唯一原因就是为了简单,也就是"让程序员免于很多技术问题带来的困扰".所以它提供了虚拟机,提供了GC,提供了单一的继承树,然后除去了C++中难以理解和使用,以及容易误用的特征.一切的一切,都是为了简单.
但世界上没有免费的午餐,为了这些特征,它必须付出某些代价,比如性能,比如某些无法涉足的问题域.我相信绝大多数Java程序员都会说,That's worthy. I agree, too.但我们不应该因此就认为Java是一种比C++更加优秀的语言,只能说,它们是面向不同的目标的不同的语言.在不同目标下,它们得到的评价是不同的.
由于Java提供了虚拟机,所以它提供了统一的执行环境,这样,对于GC的支持就可以交给执行环境.所以相对于C++,Java提供GC要容易的多.
Java不支持析构函数(finalize是一种不一样的东东),不是因为它不想,而是因为它不能. 正像我们之前所说的,析构函数是一种非常有效的管理资源的机制. 但由于Java支持GC,程序员无需知道内存何时被释放,但同时也意味着程序员无从知道对象内存何时释放.所以,根本无法提供一种行之有效的方法来提供析购.
所以,对于资源的管理还是要交给程序员,程序员可以写一些类似于析构函数的家伙,然后在他认为合适的地方调用它. 但正像内存问题一样,这会引起两个问题:
o 如果这个对象还存在引用,那么由于对象使用的非内存资源已经释放,造成对象访问异常.
o 一个对象所占用的内存已经被释放,但这个对象所占用的非内存资源未得到释放.
GC所能帮你做的仅仅是内存资源的管理,其它的资源的管理仍然要靠你自己去完成.所以,不要过度夸大GC的作用,它确实能够以性能为代价帮你解决一些问题,但它无法帮你避免掉相关的所有错误.你仍然需要关心这些问题.
最后,我对此的看法是: C++和Java是两种不同的语言,它们在不同的情况下,各自有各自的优势; GC和Non-GC也一样. 作为程序员,我们需要做到的是,准确了解它们的优缺点,然后根据我们的目标,扬长避短,正确的选择并使用它们.
你可以使用这个链接引用该篇文章 http://publishblog.blogchina.com/blog/tb.b?diaryID=650127