回复: 请教各位大大关于动态内存管理的问题…… 看了你的帖子之后我才想起来,我没有说明我所考虑的应用领域……
我所针对的是网络游戏。对于游戏来说,无论是CPU还是内存,都是不能随便挥霍的东西。特别是那些想要有所创新的游戏,更要尽量节省出各种资源,为新的特性留出余地。
我的确是想要找到一种通用的资源管理办法。资源各有不同,这一点我同意,但是这不代表我们不需要通用的管理办法,也不代表我们不可能找到这样的办法。而且,所谓通用的办法,也并不是用一个一成不变的死板的机制来管理,而是在一个通用的机制基础上进行定制。我所理解的RAII,就是在内存管理这个机制的基础上来定制其他资源的管理。内存管理实现了各种内存生命期模型,RAII则把各种资源的生命期与内存生命期绑定起来,这样对于非内存的管理,我们只需要实现这个绑定过程,以及在生命期的开头和结尾的针对性操作,而生命期模型则靠内存管理来维护。我觉得资源管理最困难的不是如何申请和释放资源,而是决定何时释放什么资源。内存管理实现了对内存释放时机的控制,这解决了最大的难题,RAII使得我们不需要为每种不同的资源重复解决这个难题。由于RAII与目前的gc很难结合,所以我觉得gc在提供了一个不错的内存管理机制的同时,恶化了其他资源的管理。
不存在什么完美的资源管理模式,这我也认同。但是,一方面,目前对gc的批评淹没于对gc的追捧之中;另一方面,我觉得应该大力发展其他确定性高的内存管理机制,与stack等高确定性机制形成互补,我觉得这种局面比gc包揽全场要更好,因为我觉得让一群各有所长的专家互相配合会比一个博而不精的人做得更好。
在特定的领域,gc不会带来太多的问题,这一点我同意。但是对于游戏等性能敏感的应用,优化仍然是有必要的。当然其可行性就要看能不能发展出更多的像stack那样优秀的高确定性内存管理机制。
关于空指针,我觉得本来是不需要到处检查的,因为完全可以做个类把指针封装起来,并且对对象进行统一的生命期管理。再结合异常,把出错情况(如访问空指针)集中在一起处理,应该也不会带来太多麻烦。手工编写大量的重复性的代码来进行有效性检测,这似乎更像C而不是C++的做法。另外曾经听别人说过这样的感受:他们觉得随着经验的增长,对new/delete的调用在不断变少,并且也没有使用gc。如果这是真实的、具有一定普遍性的,那么我觉得这从侧面印证了stack等内存管理机制的有效性。
至于拷贝语义,我觉得这是好的。拷贝语义实际上具有解除耦合的作用,并且在性能上不一定是无法接受的,毕竟gc也可能会进行对象的拷贝。另外拷贝语义也能提高程序访问的局部性。所以我觉得一开始大量使用拷贝语义也是可以的,在确保正确性之后再根据profile的结果来进行局部的优化,最终也可以在性能上有所确保。我觉得如果性能能够接受的话,应该优先使用拷贝语义。如果说存在一些性能之外的理由来阻止使用拷贝语义,那么我目前能够想到的只是一种情况:当需要在一个模块内直接修改另一个模块的状态时,这时因为没有接口可用,所以无法使用拷贝语义,但这个情况说明模块间的耦合度是很高的。从这个角度看,如果没有明确的性能方面的理由(时间或空间),却有大量的接口无法使用拷贝语义,那么说明设计上出了问题。
我对C#并不了解,但是我知道,即使同样使用C++编写的程序,也会有快有慢、有好有坏。当一个人从C++转向C#之后获得了收益时,可以理解为C#这个语言写出的程序的平均水平高于C++的平均水平,也可以理解为两种语言的能力的下限之间的比较,或它们的上限之间的比较,或者掌握的难易程度的比较。所以在获得更多更详细的信息之前,我觉得这样的比较很难说明什么具体的问题。
关于硬件带来的性能提高问题:有一种观点,觉得软件优化的最好手段就是什么都不做,等上18月后更换更好的硬件。这话是有道理的,但是对于用户来说,他们要花费额外的金钱;对于硬件开发人员来说,这是软件开发人员在推卸责任。现在单个CPU内核的性能提高已经非常困难,于是开始以添加内核数量的方式保持硬件性能的增长速度。但是如果软件开发人员不进行针对的设计和优化,那么单线程的任务就无法从硬件升级中获得收益,硬件开发人员再也不能当作挡箭牌了。
应用范围的确是个问题,之前的讨论一直没有明确这一点,这是个失误~ ^_^!!我个人关注的还是那些性能(时间和空间)敏感的应用。不过,我看不出stack有什么无穷的“隐患”,如果它不被误用的话……
另外,我思考这些内存管理问题的原因之一,就是看到越来越多的观点,他们认为gc对其他内存管理机制的替代性越来越高,所以我需要考虑是否应该全面拥抱gc,是否应该把gc作为最主要的内存管理手段。我个人的结论是:不行,至少目前还不行,至少目前在游戏领域内还不行。 ^_^!! |