返回   cpper编程论坛 > 技术杂烩
注册账号 论坛帮助 会员列表 日历事件 搜索 今日新帖 标记版面已读

技术杂烩 找不到地方的技术问题?这里!

回复
 
LinkBack 主题工具 显示模式
  #41 (permalink)  
旧 2008-02-24
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

-_-!
不过这里的水平还是很好滴,我是能学到很多东西滴。
可惜帖子是少了点
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #42 (permalink)  
旧 2008-02-28
普通会员
 
注册日期: 2003-08-25
帖子: 80
fixopen 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

感觉sjinny的想法很好。其实现在也有这方面的研究。

归根结蒂是我们需要性能。而动态决定(GC)显然比静态决定性能低。当然,动态决定才用的技术跟静态决定才用的技术也有区别。动态决定是蛮力列举,找到垃圾(或者矿石),静态决定却一般依赖于源码分析。

源码分析最有活力也最流行的方式就是在编译器中嵌入的类型系统。Titanium语言所用的类型推理技术分析指针的辖域,能够作出比程序员手工调试程序更好的决定。ML Kit编译器用一个强有力的区域推论算法来代替大部分基于堆栈的内存管理方式的垃圾收集。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #43 (permalink)  
旧 2008-02-28
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

如果是对源码进行自动化的静态分析,那么还是跟我所希望的不一样。我所希望的是提供栈以外的新的逻辑模型来填补栈的表达力所不及的那些空白,我觉得这是在简洁性和性能之间取得平衡的理想手段。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #44 (permalink)  
旧 2008-02-28
高级会员
 
注册日期: 2002-09-15
帖子: 2,531
ajoo 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

引用:
作者: fixopen 查看帖子
感觉sjinny的想法很好。其实现在也有这方面的研究。

归根结蒂是我们需要性能。而动态决定(GC)显然比静态决定性能低。当然,动态决定才用的技术跟静态决定才用的技术也有区别。动态决定是蛮力列举,找到垃圾(或者矿石),静态决定却一般依赖于源码分析。

源码分析最有活力也最流行的方式就是在编译器中嵌入的类型系统。Titanium语言所用的类型推理技术分析指针的辖域,能够作出比程序员手工调试程序更好的决定。ML Kit编译器用一个强有力的区域推论算法来代替大部分基于堆栈的内存管理方式的垃圾收集。
顶一下。

对呀对呀,静态代码分析才是王道。
让程序员在代码中手工加入delete是丑陋的,因为“什么时候这个对象不需要了”这个信息是代码本身就已经提供了,手工delete是冗余,增加了麻烦和出错机会。比如:
代码:
{ Foo foo = new Foo(); foo.doSomething(); } // 行3
这个代码本身就已经提供信息说:“foo在行3的时候就不需要了”,其实,如果编译器足够聪明,此处完全可以在栈上分配foo。额外再加上一个delete只是因为编译器或者运行时系统的无能罢了。

现在的gc是在运行时鼓捣,如果能让编译器在静态就把这些信息整理分析出来,然后在运行时以最优化的方式来处理垃圾,就是最理想的了。(不一定就是马上delete,内存释放操作很昂贵的说。也可能是在某个地方做一个标记,然后等待合适的时机统一清理)

当然,前提是:
1。语言一定要给编译器留出这个自由,不能让程序对编译器或者运行时的各种可能清理策略敏感。比如c++这种指针随便用就比较难,一旦某个对象给优化到栈上,程序崩溃了怎么办?
2。不能把资源生存期和内存管理混为一谈。资源管理需要确定性,一个数据库连接要在什么时候关闭,一个导弹要在什么时候发射,一点也含糊不得;而内存则不然,应该让编译器和运行时自由选择最优的清理策略。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #45 (permalink)  
旧 2008-02-29
polyrandom 的头像
超级版主
 
注册日期: 2002-09-03
帖子: 3,135
文章: 20
polyrandom 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

这的确需要语言比较“道德”。在类似于C++这样的语言里面,编译器不能把它优化为stack的。因为我很可能在class Foo上面有一个pool。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #46 (permalink)  
旧 2008-02-29
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

汗一个……我一直认为,程序员永远比编译器更了解自己的程序……如果静态分析会从根本上剥夺程序员的控制权,那我觉得这样就不好了。把程序员从底层解放出来,从更高层进行控制,这我还是能接受的。
我觉得针对人来开发新的方法会比针对程序开发新的编译器更重要。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #47 (permalink)  
旧 2008-02-29
普通会员
 
注册日期: 2003-08-25
帖子: 80
fixopen 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

引用:
2。不能把资源生存期和内存管理混为一谈。资源管理需要确定性,一个数据库连接要在什么时候关闭,一个导弹要在什么时候发射,一点也含糊不得;而内存则不然,应该让编译器和运行时自由选择最优的清理策略。

数据库连接什么时候关闭其实跟导弹什么时候发射还是有不同的。

所有的资源,只要不影响下一次使用(或者别人的使用[考虑并行运行环境]),都可以延后释放。

这个想法是不是太过激进?不过我倒是觉得很自然,毕竟,只要在我看的时候,前面所有的操作的效应都生效了,我就不抱怨什么了。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #48 (permalink)  
旧 2008-02-29
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

延后释放,其实是把释放的压力集中在了某一次申请之前,我觉得这样不好。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #49 (permalink)  
旧 2008-02-29
普通会员
 
注册日期: 2003-08-25
帖子: 80
fixopen 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

有些东西,一次释放多个跟多次释放的消耗是不同的。
比如STL容器,一次insert多个,跟多次insert一个,其代价一般是不同的。

大多数同质的资源在组织的时候都是组织成链表什么的,所以延后释放会有一些优势。

更何况,延后释放可以在运行时作出,根据运行时的负载状况,找到更合适的时机干这个不产生效益的脏活累活还是有好处的。
当然,这样一来,似乎又回到GC的老路上了。不过,我这儿并没有要求compiler把已知的信息都扔掉,然后运行时的gc再一次的恢复这些信息,它们完全可以协作完成这个事情。

对了,我强调一下,sjinny说的信息丢失是我强烈认同的。使用关系图在编译期就能得到一定程度的构建,不需要在运行时完全从零开始构建。着也是sjinny比较喜欢refcount的原因,因为,毕竟,程序逻辑中表达了的使用关系图,在运行时也采用了。当然,我不是说refcount就好,但是某种改进的应该不错,并且显然不会影响程序的局部性。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #50 (permalink)  
旧 2008-02-29
普通会员
 
注册日期: 2003-08-25
帖子: 80
fixopen 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

不知怎的,我头脑中出现了“事务”这个概念。

或许是:批量操作跟单个操作的粒度不同导致我思想偏向了“原子性”这个范畴吧。

对于并行运行环境,似乎我们必须越来越重的依赖于运行时而不是编译时,编译时可以提供的信息也就是提供事务的边界信息和同步条件信息,其余的别的信息比如某些对象什么时候释放之类的东西都不在是静态可以确定的了,都只能依靠runtime来确定。

看来,GC系统还是很有必要的,当然需要大大加强。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #51 (permalink)  
旧 2008-02-29
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

理论上把资源释放放在低负载期自然是个好事,可是实际运行时很难知道将来的负载变化情况,特别是那些负载和用户有较强关联的程序,我们无法假定用户将来会怎样或不会怎样。所以延后释放会增加风险。
我也并不完全排斥gc,只不过如果gc要充当日常的管理手段,我会比较反感。

我觉得并行环境并不一定导致对运行时信息的依赖的增加。我觉得导致这种增加的原因主要还是编译器信息的缺失,一种缺失是主动丢弃引起的,另一种则是因为业务逻辑本身而导致的,比如要根据用户的决定来做出一些选择。如果从模块化的角度看,以后一个程序在编译器对信息的掌握(的比例)会越来越少,所以说以后对运行时信息的依赖会增加我也认同,只不过原因不一样。

另外,gc作为一种“底线”是好的,但是如果把日常水平降低到底线,那就是种堕落。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #52 (permalink)  
旧 2008-03-01
普通会员
 
注册日期: 2003-08-25
帖子: 80
fixopen 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

不妨反过来想想,在编译期,究竟有多少东西是可以静态确定的?

Fortran似乎静态确定的东西很多,但是递归就不能支持了。

对于并行,本质上是一种运行模式,编译期静态可得的信息究竟有多少?其实只有那些同步框而已。我们没有办法预测那个活动比另一个活动早一步到达目的地,只能给出一个信息就是在某点两人汇合或者某点A停止B开始等这些同步信息。

我们把模板和实现推广以下,可以认为编程是制造模板,运行程序是使用模板制造产品。制造模板的时候,我们考虑了产品的特征,也唯有如此,我们才能造出来这个模板。可是,用模板制造产品的时候,总会有很多很多我们制造模板的时候不需要考虑或者没有考虑的事情,原料不够【CPU运算性能低,文件不存在……】怎么办?原料不符合设想的要求怎么办?一系列问题。模板是一个静态可确定的东西,而制造出来的产品只有在制造后才确定出来。

当然,我并不是反对静态的收集程序特征信息,甚至运行时的特征都可以有所反应。实际上,类型系统就有这个作用,可以帮助程序更高效和安全的运行,但是,类型系统是有其极限的,最经典的例子是现在的类型系统都没有办法处理我在一排动物窝里面奇数号窝放乌龟,偶数号窝放螃蟹的这个事实。它们都没有办法描述这个模式。类型是静态确定的,对象却是动态确定的。而生命期的问题在顺序执行环境下也是可以确定的,但是在并行执行环境下却不可静态确定。

我不排除经过深入的思考,拼命的努力,可以把本应该静态能确定的东西确定下来,Fortran编译器就是一个实例,但是问题是是否值得?确定下来的好处是性能高,可是灵活性也相应的降低,这种取舍是否划算?并且,你不觉得有些东西是没有办法静态确定的吗?你或许会想到C++的TMP也是图灵完整的,似乎就意味着我们可以静态确定所有的事情了。但是实际上你别忘了,C++的TMP根本不能处理IO,甚至所有实施了Immutable的FP语言实现IO都显得很绕并且性能确实成问题。更别说所有的都静态确定了。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #53 (permalink)  
旧 2008-03-01
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

关于“在一排动物窝里面奇数号窝放乌龟,偶数号窝放螃蟹”,似乎只要把相邻两个窝封装一下,那么就可以了。
代码:
class Pair { public: WuGui _wugui; Pangxie _pangxie; }; ... Pair wos[max]; ...
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #54 (permalink)  
旧 2008-03-01
高级会员
 
注册日期: 2002-09-15
帖子: 2,531
ajoo 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

静态分析当然不可能分析出所有的东西了,但是如果在一些语言规则的帮助下,前景还是有的吧?

比如现在就有技术可以分析一个局部变量时不时漏到scope外面了,如果没有,放到栈上就没问题呀。这个只是对内存说的,不关io什么事吧?而且也不仅仅局限于类型系统。



引用:
作者: fixopen 查看帖子
不妨反过来想想,在编译期,究竟有多少东西是可以静态确定的?

Fortran似乎静态确定的东西很多,但是递归就不能支持了。

对于并行,本质上是一种运行模式,编译期静态可得的信息究竟有多少?其实只有那些同步框而已。我们没有办法预测那个活动比另一个活动早一步到达目的地,只能给出一个信息就是在某点两人汇合或者某点A停止B开始等这些同步信息。

我们把模板和实现推广以下,可以认为编程是制造模板,运行程序是使用模板制造产品。制造模板的时候,我们考虑了产品的特征,也唯有如此,我们才能造出来这个模板。可是,用模板制造产品的时候,总会有很多很多我们制造模板的时候不需要考虑或者没有考虑的事情,原料不够【CPU运算性能低,文件不存在……】怎么办?原料不符合设想的要求怎么办?一系列问题。模板是一个静态可确定的东西,而制造出来的产品只有在制造后才确定出来。

当然,我并不是反对静态的收集程序特征信息,甚至运行时的特征都可以有所反应。实际上,类型系统就有这个作用,可以帮助程序更高效和安全的运行,但是,类型系统是有其极限的,最经典的例子是现在的类型系统都没有办法处理我在一排动物窝里面奇数号窝放乌龟,偶数号窝放螃蟹的这个事实。它们都没有办法描述这个模式。类型是静态确定的,对象却是动态确定的。而生命期的问题在顺序执行环境下也是可以确定的,但是在并行执行环境下却不可静态确定。

我不排除经过深入的思考,拼命的努力,可以把本应该静态能确定的东西确定下来,Fortran编译器就是一个实例,但是问题是是否值得?确定下来的好处是性能高,可是灵活性也相应的降低,这种取舍是否划算?并且,你不觉得有些东西是没有办法静态确定的吗?你或许会想到C++的TMP也是图灵完整的,似乎就意味着我们可以静态确定所有的事情了。但是实际上你别忘了,C++的TMP根本不能处理IO,甚至所有实施了Immutable的FP语言实现IO都显得很绕并且性能确实成问题。更别说所有的都静态确定了。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #55 (permalink)  
旧 2008-03-02
cat cat 当前离线
高级会员
 
注册日期: 2003-11-06
帖子: 1,560
文章: 6
cat 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

引用:
作者: ajoo
对呀对呀,静态代码分析才是王道。
让程序员在代码中手工加入delete是丑陋的,因为“什么时候这个对象不需要了”这个信息是代码本身就已经提供了,手工delete是冗余,增加了麻烦和出错机会。
王道的说,能优化的优化掉,剩下的交给GC. 这样的代码可能比手写的还好。

引用:
作者: sjinny
汗一个……我一直认为,程序员永远比编译器更了解自己的程序……如果静态分析会从根本上剥夺程序员的控制权,那我觉得这样就不好了。把程序员从底层解放出来,从更高层进行控制,这我还是能接受的。
编译器产生的汇编还是很强劲的,会有不少妙手。如果不是对汇编极其了解(比如housisong)很难写得出,或者作进一步优化。不是每个程序员都有精力到那个程度的。你能确定内存不是这样吗?

引用:
作者: sjinny
延后释放,其实是把释放的压力集中在了某一次申请之前,我觉得这样不好。
各种不同资源性质不一样的。对于DB connection如果每次用好都释放掉会很慢的。所以要connection pool.
内存也一样,太积极太确定的话,你的一个delete导致C++ Runtime发现一个page没人用了然后直接还给OS下次再申请恐怕效率也高不了。
所以说极端完美的确定恐怕并不能达到你想要的效果。

此帖于 2008-03-02 01:15 AM 被 cat 编辑.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #56 (permalink)  
旧 2008-03-02
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

编译器的优化我不是很懂,但是如果那些优化是普通人所难以理解的,而程序员的意图也不是编译器能够完全理解的,那么这样的优化会给可靠性带来麻烦。至少我相信,编译器再怎么智能也无法从代码中完全推测程序员的意图。
如果在java中,我一直持有一个指向垃圾的引用,那么gc该不该回收它?gc能像程序员一样理解程序吗?或者比程序员更好的理解程序?如果不能很好的理解程序和程序开发的意图,那么这样的优化靠得住吗?


如果我发现某些资源需要延后释放,那么大可以用内存池之类的机制来做。可是如果我发现原先交给gc挂历的某些资源需要更及时的释放,我该怎么办呢?之前我对资源的生命期不是很了解(因为使用gc,或者因此使用gc),那么现在我能做什么呢?如果我了解一个东西,那么我能改进它。如果我不了解,那就什么都做不了了。

我说的确定性并不是说静态稳定性,并不是说要一成不变的东西才算高确定性。确定性是指真对于程序员的确定性。所以对于智能指针,一方面它的释放时间有未知性,但是另一方面,程序员对它的释放时机还是能有所掌握的,毕竟当没有人使用它时它一定会被销毁。可是gc呢?如果不手动发起collect而交给gc,程序员对它的释放时机就很难把握。
还是之前的话,技术应该帮助程序员更好的理解和把握自己的程序,而不是反过来让程序员越来越难以把握自己写的程序。if、for、while这些结构帮助程序员更好地组织代码运行流程,从而让程序员更容易理解程序;而有些技术则恰恰相反,有点杀鸡取卵的味道。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #57 (permalink)  
旧 2008-03-02
cat cat 当前离线
高级会员
 
注册日期: 2003-11-06
帖子: 1,560
文章: 6
cat 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

引用:
作者: sjinny
如果在java中,我一直持有一个指向垃圾的引用,那么gc该不该回收它?gc能像程序员一样理解程序吗?或者比程序员更好的理解程序?如果不能很好的理解程序和程序开发的意图,那么这样的优化靠得住吗?
GC不用猜,他就是不回收。编译优化只作他确定与原来程序语义完全等价的步骤。比如ajoo说的,如果一个局部变量你new完没有传出去变量的生存期就结束了,那么编译器就直接把它做成一个stack变量就是一种优化。
至于为什么有些优化程序员会算不清楚,因为优化过的代码非常难懂,但寄存器布局最优计算用的指令最少。
引用:
作者: sjinny
可是如果我发现原先交给gc挂历的某些资源需要更及时的释放,我该怎么办呢?
call System.GC.Collect() 各种资源之间是不同的,你要让GC来管理那些本应该你自己管的东西就不对了。
引用:
作者: sjinny
所以对于智能指针,一方面它的释放时间有未知性,但是另一方面,程序员对它的释放时机还是能有所掌握的,毕竟当没有人使用它时它一定会被销毁。
心理问题……其实你上面那句话看出来,用不用gc其实都不知道它什么时候释放内存。所以其实区别不大。可能你这里函数退出的时候就有一个dtor然后连带了好多好多dtor释放智能指针, 结果卡一下。这和gc突然介入相比感觉上也好不到哪里去。
如果你这里关心的是对象的销毁,那么用智能指针也不是一个好的选择。原因如上,你还是不知道它什么时候销毁。可能你不小心引入了一个循环引用,或者不小心多copy了一份。那么就没人帮得了你了。你甚至很难知道这个多出来的引用在哪里。对于这种类型的东西,用using这种scope管理,或者来一个pool集中管理,到时候把pool销毁等,都比用智能指针“确定”得多。如果你觉得pool销毁了内存还没回收,你也可以在那里写一个GC.Collect(). 虽然我觉得大多数情况下并不必要。GC比你清楚内存还剩多少。
引用:
作者: sjinny
还是之前的话,技术应该帮助程序员更好的理解和把握自己的程序,而不是反过来让程序员越来越难以把握自己写的程序。if、for、while这些结构帮助程序员更好地组织代码运行流程,从而让程序员更容易理解程序;而有些技术则恰恰相反,有点杀鸡取卵的味道。
隐藏细节恰恰帮助程序员更好地把握程序。GC就把和内存相关一些细节隐藏在了你背后。如果这些细节恰巧对你来说不重要,那么GC就大大帮助你集中注意搞设计。就像高级语言把寄存器布局,CPU流水等等都藏了起来。CPU也没告诉你你的指令可能会被乱序执行(可能有些编译优化器知道,并且会有针对地作优化)。

此帖于 2008-03-02 08:42 PM 被 cat 编辑.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #58 (permalink)  
旧 2008-03-03
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

说道优化,有个题外话:我一直到最近才明白,程序的优化,首先是针对人的资源的优化,其次才是针对机器的优化。首先要优化的就是尽量少占用人的资源,让人更容易理解、把握,占用人尽量少的脑力、时间和精力,哪怕为此多花一些机器时间也是值得的,毕竟无论是程序开发人员,还是软件使用者,他们的脑力资源比机器的资源更宝贵。

就针对机器的优化来说,我一直坚持,如果一种优化会使程序员对程序的掌控减弱,那么就是不合算的。

手动调用gc的collect,恐怕也不能频繁地这么做吧,毕竟频繁扫描内存的话代价还是很高的。至于“本来应该自己管理的资源”,这个我还不清楚怎么界定。毕竟内存也可以说是“本来应该自己管理的资源”,毕竟程序员要对程序负最终的责任,如果内存管理出问题,责任不会落在gc头上。我只是觉得,如果把gc看作多种管理手段之一,由于它的不确定性(在怎么样可控性也比内存池要差),以后的优化会受到负面影响。

智能指针的释放“时间”自然是由runtime决定的,甚至根本上说释放“时机”也是由runtime决定的,而后者正是不得不使用智能指针的原因。但是另一方面,我知道当没有指向这个对象的指针时这个对象一定会立刻被释放,而使用gc时我不知道是否会这样。如果需要我手动collect,那我不如使用内存池。如果我不能使用内存池,那么意味着我即使使用gc,我也很难知道应该什么时候collect,因为释放的“时机”是由runtime决定的。
一个对象的释放引起大量对象释放的问题,其实gc也有,毕竟本质上gc也是靠“对象是否被需要”来判断是否应该回收的,只不过gc没有智能指针里那么多析构函数调用的开销,但是gc有一些开销是智能指针所没有的,毕竟扫描内存,比如线程切换,而且当gc扫描一块内存时应用程序自然无法再使用这块内存。
其实我最关心的是,我是否知道我所采用的技术到底在干什么。其次才是对象如何以及何时被销毁。
对于内存池和gc之间的选择,我也会优先选择内存池,就像我顶楼里写的,我心中的优先次序是:
栈>内存池>智能指针>gc
能用栈我就不用内存池,能用内存池我就不用智能指针,能用智能指针我就不用gc,只有其他都不能用的时候我才选择gc。但是,gc有个特别的地方就是,它的表达能力近乎无限。栈和内存池其实都对内存的时候有着各自的“假设”,所以当我需要一个对象的生存期跨越栈时,如果不让它升到更高层的栈,那么就会打破栈的使用假设,内存池也一样。对于gc,如果不考虑性能,只考虑“能不能”的问题,那么我还不知道有什么情况是无法应用gc来表达的,只不过性能和确定性等问题使gc在我心中的地位排到了最后。如果gc既有现在的表达能力,又有栈的性能,而且在需要的时候我能够容易的预知它的行为,那么我就会把gc放到第一位了。 ^_^!
其实,如果gc的释放时机能够达到智能指针的确定程度,那么我就会把gc放到智能指针的前面。如果要靠手工collect,那么不如换成内存池。


关于隐藏的问题。我觉得,良好的设计应该是有透明性的,也就是说加入这个技术之后,它自己会做一些事情,但是我不需要知道它做了什么,但是更重要的是,我原来知道的事情,在加入之后我仍然知道,而不会被新技术挡住视野。就像玻璃窗,它能帮我挡风遮雨,但是我仍然能看到窗外的景色。
如果内存真的是不需要关心的问题,那么隐藏起来也无所谓,但是恰恰相反,尽管现在的内存越来越大,但是内存仍然是常常不够,一方面是因为人的需求在不断增长,当硬件的处理能力提升了10%时,人的期望却可能提升20%,所以我从来不认为硬件资源会“足够”,另一方面,延后释放会加剧对内存的需求。所以内存管理决不是程序员可以放手不管的事情,除非gc真的具有了程序员那样的智慧。
而CPU的优化,其实在很多时候仍然会需要使用汇编进行优化,而现在很多程序都在引入一些多媒体技术来改善用户交互,所以CPU使用的优化也不是程序员可以放手不管的问题;而乱序执行和寄存器使用的优化恰恰使得多线程程序(特别是多核机器上的多线程程序)更难编写,所以C++09才需要做出一些限制。虽然我对多线程不是很推崇,但是它毕竟是多核时代优化的重要手段。
其实,如果需求是一成不变的,那么很多隐藏都不会带来太大的问题,毕竟如果它过去能生存下来,那么以后也能。但是当需求经常会变的时候,一项技术过去运行得再好也不能说明以后会怎样。而当需求变了,我们却发现很多东西都被层层遮盖起来,这时我们要适应需求的变化就会有更多的阻力。人对程序的了解越多,那么就越容易适应变化。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #59 (permalink)  
旧 2008-03-03
普通会员
 
注册日期: 2003-08-25
帖子: 80
fixopen 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

引用ajoo:

静态分析当然不可能分析出所有的东西了,但是如果在一些语言规则的帮助下,前景还是有的吧?

比如现在就有技术可以分析一个局部变量时不时漏到scope外面了,如果没有,放到栈上就没问题呀。这个只是对内存说的,不关io什么事吧?而且也不仅仅局限于类型系统。

是的。我的那段话并不是要否定静态分析,只是想跟sjinny描述一下人的局限性,人(程序员)写程序的时候只能静态确定,所以并不能很清楚的知道某些行为是不是最优的。

引用自sjinny:
说道优化,有个题外话:我一直到最近才明白,程序的优化,首先是针对人的资源的优化,其次才是针对机器的优化。

如果你明白这个,再想想“软件开发的任务就是管理复杂度”,你就知道你反对GC是不对的了。正如cat所说:

隐藏细节恰恰帮助程序员更好地把握程序。GC就把和内存相关一些细节隐藏在了你背后。如果这些细节恰巧对你来说不重要,那么GC就大大帮助你集中注意搞设计。就像高级语言把寄存器布局,CPU流水等等都藏了起来。CPU也没告诉你你的指令可能会被乱序执行(可能有些编译优化器知道,并且会有针对地作优化)。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #60 (permalink)  
旧 2008-03-03
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

静态信息的确是不完全的,但是有些东西最好还是让程序员自己来说明。比如说栈,你可以根据代码进行静态分析,让程序自己得出结论某个变量有明确的作用域。可是那只是一种结果,却不能反应程序员的意图。如果像C这样显式地说明栈,那么说明的就是程序员的意图:我不想让栈里的这些变量的作用域泄露到栈外。栈相当于微观上的模块边界,如果这些都要让程序去检测、去猜测,那么显然就是让程序去决定微观模块边界了。如果需求不变,那么代码形成的事实上边界就是程序检测出的,以这个边界来组织目标代码是没问题的。但是如果需求会变呢?当代码写完之后过了一段不长不短的时间后,当人不记得程序的细节之后,一段代码里如果没有程序员给出的显式边界,那么如何理解程序中各种变量的意图?即使读代码的人能够看出代码里事实上的边界,但是按照原先的设计,设计意图中的边界却得不到体现,于是就只知道这个变量目前的生存期可以压缩到某个范围,却不知道在这个范围之外对它的使用是否符合原先的设计,而且如果使用了某个超出了设计中的边界的变量,那么这时编译器是无法检测出来的,于是编译器本来能够检测的错误就无法检测出来了。如果代码里不能承载设计信息,那么也许一时之间没有问题,但是当一段时间之后,人遗忘了设计信息之后,这段代码就变得难以维护了。任何程序,也许能够了解这段代码目前的情况,也许能用各种技术进行扫描和推断,但是这些技术无法从根本上理解需求,所以它们获得的信息同样是不完全的。也许人对代码的了解不如这些分析程序多,但是人对需求的了解绝对多于这些程序,其实我认为这些程序无法理解需求。一段代码,如果要复用它,那么就要知道使用它的前提条件,以及使用它时要为它提供什么样的环境,所以一段代码自身附带的设计信息越多,越容易让人理解它,那么就越容易复用。

而关于隐藏,仍然有个维护性问题。人对一个东西了解的越少,那么就越难以理解、维护、修改和复用它。乱序执行CPU指令给多线程程序的编写所带来的麻烦已经是个事实了,已经影响了语言标准的修订。
另外,各种技术本身是有结构成本的,要用gc来隐藏内存管理问题,那么人就要学会使用gc,就要理解gc,可是由于gc的行为和具体的runtime有关,所以理解它的机制不难,但是要理解某次应用程序崩溃过程中gc的行为那就难了。gc本身是靠不确定性来应对不确定性的,所以我觉得gc能力的增强往往会带来更高的不确定性。gc这样的技术,在隐藏了内存管理之后又引入了新的问题,引入的就是gc自身的不确定性。


我并不是要完全否定gc,我也说过,当栈、内存池和智能指针都不能满足需要时,我会选择gc。所以我并不是呼吁丢弃gc技术。我要呼吁的是,gc始终是无可选择时的选择。我认为现在最需要的是完善确定性的管理办法。用各种不同的方法来互相填补表达能力的空白。在我的理解之中,某些情况下栈的效果不好,这时由内存池来弥补这一空白,如果内存池也不行,就靠智能指针……就像cat说的,智能指针自身也不怎么样。所以我这些帖子的重点是,要在栈和内存池之外设计更多的高确定性机制,来填补内存池和智能指针之间的空白。按照确定性由高到底排序:
栈 > 内存池 > ? > 智能指针 > gc
现在需要设计处于?位置的机制。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
回复

书签