查看单个帖子
  #11 (permalink)  
旧 2008-02-16
sjinny 的头像
sjinny sjinny 当前离线
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教各位大大关于动态内存管理的问题……

首先我假设任何内存块的回收时机都存在一个事实上的最佳时机。过早的回收会导致程序出错,就不去考虑它了。而过晚的释放会有这样几种趋势影响:
1.倾向于占用更多的内存。
2.倾向于导致更高的cache miss概率和内存页面错误率。因为如果及时的回收,将能够使得内存占用的分布更加的紧凑,而且这种紧凑是不需要对对象进行复制的。内存回收的滞后会倾向于使内存占用铺得更开。
3.难以把其他资源的管理绑定到内存管理上。首先,不管内存怎么降价,它都不是免费的资源。第二,还有其他的资源可能比内存更加宝贵,无论是os资源、硬件资源,还是应用程序自己的各种资源,都不能假设它们便宜到“不需要尽快回收”的地步,毕竟将来产生的新资源不应该受这种假设的约束。第三,就我所知,RAII是把各种资源绑定到内存上进行管理的一种比较好的办法,但是它是用析构函数把内存的释放和其他资源的释放绑定到一起的。第四,内存释放的滞后意味着其基础上的RAII对其他资源的释放也是滞后的,更重要的是,如果没有手动的调用collect,那么gc对内存及绑定其上的其他资源的释放时机是未知的,虽然可能不会等多久,但是我们无法确认其释放是否能够做到“及时”。如果手动调用collect,但是这个collect是针对所有类型的对象的,那么各种不同生命期模型的对象会使collect的时机很难正确把握,毕竟用了gc就说明这些对象的生命期模型对于程序员来说不是非常明确的。如果collect是有很强的针对性的,那么我不认为这是gc,我会认为这是内存池。在我看来,内存池与gc的不同是,使用gc时程序员对各种对象的生命期的知识是非常少的;使用内存池时,虽然程序员无法明确各个对象的生命期细节,但是对于某个对象集合的生命期还是比较了解的,所以才能把握好释放内存池的恰当的时机。虽然内存池可以与gc的一些技术结合(比如内存扫描),但是其设计思想上的分界线在我看来还是比较明显的,并且结合后所引入的内存扫描也只是这种内存池的辅助功能而非关键。
4.gc线程与应用程序之间的切换可能会带来一些问题。如果不是增量gc,那么会引起应用程序停顿的问题。如果是增量gc,那么相对于非增量gc会产生更多的线程切换和竞争。如果可以讨论极端情况,那么一个极端是gc的频率很低,于是导致非增量gc那样的应用停顿问题;如果gc的频率很高,那么线程切换和竞争也会降低整体效率,仍然可能使应用的响应变慢。理论上如果在这两个极端之间有一个良好的平衡点,那么以上的极端问题都不会严重。但是对于应用环境会变化的应用来说,寻找这样的平衡点也是个麻烦。在以后CPU内核越来越多的环境中,我怀疑寻找这样的平衡点会越来越困难。我觉得,gc可能会在同一个线程中处理很多原本是低耦合的内存块,这些内存可能由很多不同的线程使用,它们本身没有太多关联,但是如果要由同一个gc线程来处理它们,那么就认为使它们发生了耦合,结果就是使得原本低耦合的各个应用线程因为gc而提高了耦合度,线程之间的高耦合会因为竞争而降低性能。当然这最后一点与gc的不确定性关联不大,不过也是我不看好gc的理由之一。虽然猜测的成分很大,不过毕竟让我不放心。

关于gc“丢弃信息”的问题,我举个极端的例子。就我所知,java里程序员无法在桟上建立对象,于是即使是生命期很明确的对象也需要依赖gc或者vm的自动化分析。这个例子中,程序员对对象生命期的知识就无法明确的在程序里表达出来,这就是对知识的丢弃。java的vm好像能够进行自动化的分析,但是这是先让程序员把自己知道的知识丢掉,然后再让程序来进行分析,就我所知目前即使是AI也还没有人那么聪明。


我对这些的理解也很肤浅,所以还请各位大大多多指教口牙…… ^_^!
回复时引用此帖