免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: 蔡万钊
打印 上一主题 下一主题

[技术动态] [转] 垃圾收集真的有用么? [复制链接]

论坛徽章:
5
技术图书徽章
日期:2013-08-17 07:26:49双子座
日期:2013-09-15 16:46:29双子座
日期:2013-09-25 08:17:09技术图书徽章
日期:2013-09-25 09:11:42天秤座
日期:2013-10-01 16:25:34
21 [报告]
发表于 2013-09-15 15:11 |只看该作者
有JB用,

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
22 [报告]
发表于 2013-09-15 17:26 |只看该作者
回复 20# starwing83

你还得把obj的声明放在try block外面:
SomeClass obj=null;
你还得在调用release之类的函数之前检查obj是不是null,要不就会NPE
最恶心的是,每个obj都要包一层,然后代码就会变成这样:

  1. Class1 obj1=null;
  2. try {

  3.     Class2 obj2=null;
  4.     try {
  5.       ...
  6.     } catch (e) {
  7.        ...
  8.     } finally {
  9.       try { if(obj2!=null){obj2.release()} } catch (e) { }
  10.     }
  11. } catch (e) {
  12.    ...
  13. } finally {
  14.   try { if(obj1!=null){obj1.release()} } catch (e) { }
  15. }
复制代码

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
23 [报告]
发表于 2013-09-15 17:27 |只看该作者
回复 20# starwing83

最主要的原因是,无论是RAII还是GC,都*不是*语法糖,没法通过其他机制简单的模拟出来。

论坛徽章:
3
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:58:11数据库技术版块每日发帖之星
日期:2015-08-30 06:20:00
24 [报告]
发表于 2013-09-17 00:41 |只看该作者
JAVA 这种垃圾,能在世上活的好好的,正说明垃圾程序员的数量太庞大了!

论坛徽章:
0
25 [报告]
发表于 2013-09-17 06:36 |只看该作者
回复 2# starwing83

与其关心怎么避免循环引用带来问题,之前应该先注意一下,为什么会有循环引用
“程序员仍然需要关注对象之间的关系”是在什么前提下成立的?为什么需要担心“一不小心”就是循环引用?
看来现在的应用开发者普遍漠视资源所有权。似乎认为资源天生就是已经为他们准备好而可以撒手不管什么时候可用的。殊不知这是站错了立场:甚至最终用户(比如系统管理员)都不见得有这个权利。他们自然不能指望在任何情况下明确生存期的工作都是预先被准备好的,总有谁来擦屁股——或者是自己,或者是别人,或者是GC之类用于偷懒的捷径。可惜现时GC的智能远远不如人类(它们不知道哪些资源对实际的需求而言是真正有用、需要保留到什么时候可用的)——并且恐怕很长一段时间都不会有。考虑环境限制,单纯通过引用关系决定的生存期都不见得是足够合适的,更别提GC这种放弃确定性资源销毁的策略了。

论坛徽章:
0
26 [报告]
发表于 2013-09-17 06:37 |只看该作者
回复 5# fergon


    Linus这种手贱的抖M有什么好说的。

论坛徽章:
0
27 [报告]
发表于 2013-09-17 06:45 |只看该作者
回复 7# zhouzhenghui


没这么简单。对于C++这种静态语言,没有GC不成问题(并且内建的GC实际上没什么明确的好处:强迫用户使用GC还会造成显然的问题)。但是,想象一下如果C++的lambda capture允许在运行时决定具体内容,那么没有GC或者类似物就很头疼了。
不过,传统GC仍然不见得是必要的。只要在每一个翻译单元保持一个起到GC作用的对象池类似物就够了。(注意即便提供以上的动态特性,C++翻译单元还是可以被静态确定的。)
只有彻底动态的语言(不需要区分出静态phase的语言,例如传统的Lisp方言)才有必要使用GC。不过那也只是管理meta-circular evaluator的存储而已,并不能替代一般意义上的资源管理。

论坛徽章:
0
28 [报告]
发表于 2013-09-17 07:19 |只看该作者
最后,在不必要使用GC的少数场合,GC可能也是有用的。它是一种当内存资源充足时在时间上的优化。优化掉的东西是对所有权层次的tracking导致的访问开销。如果把所有权按对象作为节点表示成树,GC的作用就是提供一个公共节点使树的高度退化到1,这样访问任意节点的间接操作数就会最小。
对于C++来说,一个内建的例子是对象决定子对象的生存期,或者说对象具有子对象的所有权。但是,对于典型的C++实现,求值a.b.c和a.b的性能差异仅仅在于编译时,而运行时无关紧要。这里GC完全没有体现出优化的优势,就很鸡肋了。
当然,更复杂的运行时决定所有权的场景,GC还是有些用的。只是现在的GC似乎都倾向于执意把所有权关系的层次减到最小而不是采取中间策略,对存储的利用低下实在非常感人……http://sealedabstract.com/rants/why-mobile-web-apps-are-slow/这篇文章就说了“If you find yourself with 6 times as much memory as you need, garbage collection is actually going to be pretty fast.”我的经验是现在真没多少非玩具程序能这样不在乎利用率的,只不过移动平台特别明显而已。此外这篇文章也指出GC实现的改进不容乐观。
我并不是十分清楚GC和确定性资源管理相对立的偏执的动机。只是因为放任纠结“易用”的烂语言设计的错觉所以才不采取中间路线?那么,为了不自讨苦吃,是时候放弃不良用户习惯和语言设计了。

论坛徽章:
0
29 [报告]
发表于 2013-09-17 13:46 |只看该作者
楼上引用说明是非常正确的,比如我在实现c-sigslot(s-sigslot.googlecode.com)时,其中的lambda表达式是包含了初始化,调用和释放三个部分,也就是资源必须绑定到一个能够被明确释放的地点。显然所有的顺序执行程序代码都可以满足这一条件,也就是说资源能否静态释放只跟并行访问有关,而跟是否动态分配没有关系,如果有库实现让你做不到这点,说明其设计本身存在问题。

并行带来是逻辑上的不确定性,所以不能以事先确定释放的时机或地点,类似的,当逻辑复杂到一定程度,一些非并行的资源也会难以被正确的释放,这也就是GC带来的好处。但是事实上,一些简单的辅助手段比如引用计数就可以帮助你克服绝大多数这样的问题。而能够在更高层次上解决问题所带来的各方面优势也是不能忽略的,根本上说,我们只需要分配大部分实际运行过程中必须分配的内存,而不需要重做大多数反复劳动,这才是正确的内存利用方法。

我们来看什么是必须分配的内存,什么又是不必须分配的。因为不知道释放的内存只跟并行有关,以生产者消费者为例。生产者产生产品必须放入临时分配内存中,而消费者不断消费内存,在消费完后释放,如果有多个消费者,这种消费完的事情是不确定的,引用计数可以帮助我们容易的确定这件事情,唯一的前提是消费者事先要加入到消费者队列中,其数量可以被确定。更加重要的是这个时候释放的内存是可以返回给生产者重用的,也就是大大节约了内存的分配释放过程的消耗,以及其他负面影响。这种可能极大优化的场景对于一无所知的GC而言,却是极大的压力。参考各种在高性能服务器领域的内存池技术也就明白为什么一般性的GC是远远不够的。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
30 [报告]
发表于 2013-09-17 15:27 |只看该作者
回复 25# 幻の上帝


    我记得这一点我前面应该提到过了,可能不太明确:是这样的,程序员本身就应该对这样的关系进行关心,这种关心本质上说就是编程的一部分(甚至是最重要的一部分)。本质上说,这就是在确立问题在计算机中实际表示的层次结构了。因此,由程序员去判断循环引用什么的,其实也不算说不过去。

问题是出在细节上。想象一下一群对象共有一个对象的情况,这时候这个对象属于谁?这时候没有GC,就必须得有引用计数了,否则你就必须完全确定必定有一个对象肯定最后释放——即使它并不再被需要。

那么,三群对象互相共有的情况呢?如果这群对象的释放时机并不清楚,而且你不能排除循环引用的【可能性】,那么引用计数的问题就很大了。况且,在【均匀平等】的对象之间决定谁强谁弱也是很困难的事情,有的时候不得不提升对象的层次,从而强制有一个对象是“主”,从而能够管理下属。

发现问题了么?对。如果没有引用或者指针,问题会很简单。就如同你所说,对象关系是一棵树,但是,事实上对象关系往往会是一个图,而且是天然的有向图,有时候要模拟无向图,你都要付出代价(比如说,双向链表)。在循环有向图里面,深搜是会出问题的。而,图,你自然无法完全保证,你就一定能够分清楚其节点的主次强弱和顺序——还有莫比乌斯环那种东西存在呢。

这时候,采用引用计数模型就遇到了困难,我现在知道有两种解决方案:一种是前面有人提到过的block_ptr,一种则是强行添加新的节点,在代码中添加明确的主次关系。后者是通常选择的方式,C++的臃肿层次关系即由此而来。什么XXXXManager,XXXXHandler,估计你自己也见得多了。

倒不是说这个时候GC就是救世主了。只是说,的确引用计数技术是有问题的,这个问题道理上来说是的确可以由程序员来注意,根据业务逻辑给出强弱引用来解决的。你的确可以一推三五六,全部指责程序员写代码不小心——本来就是这样——但是,你也得考虑引用计数技术本身的适用范围——它的确不适合大量平级对象存在的场合。而很多典型的应用都会存在大量平行对象——网游服务器、超算调度器、中央控制器,等等,对于这些系统应该怎么办呢?

还有,你说的GC的目的是降低对象层次的关系,提高a.b.c的存取效率。我表示不解。从我目前看过的脚本语言代码和Scheme实现来看,GC基本上没有在这个方面起到任何积极的作用。GC的目的只是在于【在重新定义对象关系时】避免频繁的内存主从判定,问题是,在【访问】的时候,GC并没有提供任何帮助。它的确采用了一个链表取得所有对象,但那个链表和malloc的freelist一样,是和逻辑没有关系的。你也无法访问到这个细节。所以希望再说清楚一些。

至于C++,有GC保底是好事,没GC也没什么大不了的。从这一点上说,的确是软肋,现在有了block_ptr更觉得GC没什么必要了。C++嘛,毕竟比较特殊,恩。

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP