| |||
| 嘿嘿,我不好事。 引用:
个人觉得资源管理,大多数也就是程序员需要指定什么时候申请什么时候释放,和程序员不需要指定什么时候申请和释放。后者显然方便,而且在后者的性能或者坏处不大的情况下会被认为是比较先进的。 对于需要指定什么时候释放的,stack/FIFO/using只能cover一些情况。还有其他情况它的生命周期是动态的,就需要分析生命周期然后去手动调用类似alloc/free的函数。这个比起完全不用管的模式要麻烦也比较容易出错。 完全不用管的模式,比如内存和CPU. 内存有gc, CPU有OS. 都不需要你写代码控制。对于CPU完全交给OS我们已经很习惯了,就是内存好像还有不少人还当宝贝一样…… 现在如果把那些general的需要手动回收的资源管理全都绑定到内存上,然后放弃自动内存管理,实际上就使得内存管理重新回到一种很可能出错的状态。但就像ajoo前面说的一样,内存管理对于进程是非常重要的,不容有错。其他资源有错,你还可能完全释放后重新初始化来恢复,但内存如果access violation之类的,OS扔给你个dump就把进程杀了。所以内存管理处错的代价和其他资源是不一样的。绑定起来也没看到其他的必要性。 如果抛弃了这个绑定,stack语义的FIFO我也一直用,有啥资源的使用都尽量往上靠。但有的时候硬靠上去的代码很难维护。 引用:
此帖于 2008-03-09 06:26 PM 被 cat 编辑. |
| ||||||
| 引用:
引用:
引用:
至于内存,现在的内存不再是宝贝了。服务器多半都是 64 位系统,地址空间以 TB 记,我还没听说有哪个应用会觉得地址空间紧张。至于买内存要钱,在今天的硬件价格水平下面,这点开销不值一提。至少,如果你不用 GC 而导致延长了软件开发周期,那么你要多付的程序员工资将会数倍于 4G 服务器内存的价格。 引用:
引用:
引用:
|
| |||
| 我觉得sjinny其实并不是对filo式的内存管理模式感兴趣,或者并不是仅仅对它感兴趣。sjinny可能是对lexical scope这类模式感兴趣,希望能把lexical scope应用到尽可能多的场合,尽可能多的资源种类上。 但是实际上,在我的感觉中,内存这种资源是所有资源中管理最复杂生命周期最有弹性的资源了。用filo管理别的资源或许还比较合适,管理内存就有点吃力了。 另外,sjinny说的{}比using的方式语法更简洁,这也只能应用在内存这种资源上,对于别的资源,我相信由于千差万别的释放资源的手法导致写出来各式各样的析构不一并比Disposing模式+Using语法简洁多少。 |
| ||||
| 唉,今天的麻烦太多,心情十分不爽…… to Elminster: 1.永远都不会有富余资源,因为我们无法假设用户在使用我们的软件的同时不会想要使用其他软件做其他事…… 2.硬件发展永远不会足够,因为当硬件性能增长了10%时,用户需求会增长得比10%更多。现在的硬件比起几十年前要强劲得太多,但是对于那些主流游戏,有多少电脑能够一直使用最佳的画质?对于那些常用软件,有多少在启动时完全不需要用户等待?无论是浏览器、office套件还是娱乐软件之类之类……有多少大型软件在出新版本时不需要用户升级硬件也能获得跟以前一样的性能?更何况很多普通用户没有能力或兴趣来优化自己的电脑…… 3.我不需要复习也知道抢占,但是这只是在线程之间的协调机制。当你的线程获得了一定的时间片后,如何使用它是你自己的事情,相信这不需要复习也不难理解。 4.如果只看服务器而不看桌面电脑,那我也可以指着超级计算机说:“因为有这样的东西存在,所以任何软件优化都是多余的”。更何况及时在超级计算机上,软件优化也是很重要的。硬件的发展是为了让用户做更多的事情,而不是为了让软件开发人员挥霍。 5.是否不使用gc就必然延长开发周期?是否使用了gc就必然缩短开发周期?是否使用了gc就不仅能必然缩短开发周期而且还必然不带来其他副作用? 6.在我看来,内存管理的麻烦的根源就在于生命期模型。如果有个机制能够确保在内存块的生存期结束时就*一定*、*准时*调用我所指定的行为,并且对这行为没有太多限制,那么内存管理还有什么难处呢?特别是对于应用程序,剩下的无非是内存池之类之类的,这和其它非内存资源的管理比起来又能困难多少? 7.不知道那些设计gc的人有没有信心说“彻底”解决内存管理问题。 8.如果某个非内存资源是信用卡读写器(哪怕只是使这张卡作废也会让用户恼火的),如果某个非内存资源是指向某个机密数据库的连接,如果某个非内存资源是指向某个人的照片目录的文件描述符而错误的访问导致其内容被发送至网络或打印机…… 9.如果某些网络连接管理出错,导致不同用户之间的数据包的混乱,那么可能多个用户的账户信息会被搞乱,而这在运行时不会有什么明显的征兆。如果内存访问导致程序崩溃,大多数用户只会习惯性地重启软件,可是其他方面的正确性用户往往不会每次都检查的……如果你能保证软件交付给用户后100%不出问题那当我没说。 10.“它们唯一的共同点只有都需要分配、都需要释放”。这就是关键:如何确保在最合适的时机执行“分配”?如何确保在最合适的时机执行“释放”?如何确保这些行为在一次生命周期中被执行一次且仅一次?如何确保在“分配”前和“释放”后的访问都会导致一次可控的异常抛出而不是整个系统挂掉?而无论是内存管理还是非内存管理,这些都是最大的麻烦(我不知道有没有“之一”)。如果你觉得非内存资源的管理不重要那当我没说。 11.析构函数最大的好处在于:它会被自动调用。如果把这个特性去掉了,那跟普通函数还有什么区别?貌似Dispose还比这种“析构函数”强点。using这种办法来实现RAII,其实是使用一种特定的方法来解决特定的问题(即实现RAII)。现实世界的复杂度太高,所以我们需要的是能够有一定程度的通用性的手段。 to fixopen: 的确,某个具体的内存管理方法是否是FILO的对我来说一点都不重要。重要的是能不能找到足够多的方法,并且它们能够用彼此的长处填补彼此的空白,从而形成一个有足够覆盖力的工具集。 我不太清楚“lexical scope”的确切含义。如果是特指栈,那么我要说我并不是想把栈用于一切。如果是泛指语法层面的模式,那么我要说我要的不仅是语法上的简洁有效,我同时也需要其背后的机制简洁有效。所以在栈和using这两者之间,我更喜欢栈。 就像我在上面to Elminster的10中所说,内存管理和其它非内存资源的管理的最大的麻烦来源之一就是生命期管理,而且现在普遍使用的生命期模型都是“分配-释放”这种最最简单的模型。所以在我看来最最关键的只是生命期问题,至于生命期结束时如何把内存还给系统这种事情并不是最大的麻烦。而生命期问题是多种资源管理的共同的问题。 至于FILO,那只是多种工具中的一种,我不记得哪个帖子里想要用它解决所有问题过,我只记得我说过很多次,这玩意的表达力有限,所以需要更多的工具来覆盖表达力盲区。 有一点我要强调:我寻求栈意外的管理手段是因为栈的表达力有限,它在它的覆盖范围之外的效果不好。如果能够使用using,那么也能够使用栈;如果能够使用栈,那么应该也能够使用using。但是问题在于,当无法使用栈的时候,能使用using吗?我觉得不能,因为不能使用栈的情况一般是程序员对生命期模型的了解不是很清楚,或者生命期模型与栈不相容。我要的是除了栈意外的、同样优秀但是表达力有所不同的工具,using不是这样的工具。我不想再争执“栈和using哪个更好”这个问题,因为这不是我最主要的问题。但是我还是要解释一下,我说using没有栈简洁,是因为使用栈时只需要使用花括号,而不需要按using这么多键。就像“x+y”这个式子比“横坐标加上纵坐标”这句话更简洁一样。 这个长贴再一次让我对一个问题加深了认识:“人与人的沟通是很困难的。” 我很多回帖都是在不断地解释大家对我的误解,而我真正想讨论和解决的问题却一直没有进展,也许我的表达能力实在太糟。 此帖于 2008-03-10 05:51 PM 被 sjinny 编辑. |
| |||
| 我印象比较深的是你上来就bs GC但GC是我见过最舒服的内存管理方式了。 管理对象生命周期的模式不少,但各不相同。自动释放也是其中一种,很方便,但实现起来却有难度,特别是对于内存这种对性能要求极高的东西。但由于内存管理太重要了,前人也画了太多的代价在上面,因此还是有人做出了在工业上可用的GC. 硬件的发展是会造成区别的。为什么DOS不支持多任务而UNIX上手就是进程模型?因为PC的CPU不够强劲。不知道Windows 3.1只有合作式是不是也有类似的考虑。当CPU足够强劲,允许OS每10毫秒来干涉一下的时候优雅的枪战式多任务就占了上风:稳定不死机,客户代码也无须管什么时候释放CPU资源。这其实就使得CPU资源从需要显示被释放变成了自动被释放。 |
| |||
| 引用:
还有你的逻辑也很让人看不懂,不经证明地抛出一大堆的论断,“我认为”,“我觉得”就不说了。前面你还质疑优化呢,还承认自己对优化不懂呢,这又开始强调优化的重要性了。还有,攻击gc的效率,确定性什么的倒也罢了,本来就是争议性的问题,但是什么软件的可维护性,对变化的适应性,这本来都是gc的长处,居然到你这里不经解释直接拿过来就成你的武器了,真是让人不由得纳闷“这都从何说起的呢?” 要说这个坛子是你最可能找到同道,大家一起捍卫destructor,声讨gc的,可惜呀,这些固执的量子时代的爱因斯坦们可能实在不好意思附和某些同志,因为这个同志有点太象gc这个敌人那边的托儿了——犯所有能犯的低级错误,除了make sense的什么都说。 此帖于 2008-03-10 10:29 PM 被 ajoo 编辑. |
| ||||
| NND,我哪句话把“软件的可维护性、对变化的适应性”与gc联系起来了?都是因为谈到了静态代码分析的问题,我才说有些东西不显式表达的话是没法再自动分析出来的,会有信息丢失。一个主题里的回帖并不一定都是针对顶楼的帖子或者标题的,我那些针对的是前面的静态分析的话题。 “lexical scope、 副作用”这些是术语,我没见过这个术语不代表我不理解其实际所指的含义。只不过我看过太多讨论因为概念和定义上的不一致而引发不必要的争执,所以我才要问。如果真的要讨论,就要愿意解释自己的想法和概念;如果懒得解释,那就不要说。 要说证明,不知道这里谁的帖子有“证明”?都是结论对结论,我就不能这么做?再说,关于gc的缺点问题,关于静态分析的问题,我都有自己的理由和解释。 我知道,每次我回帖里写得那么多,真正能够一字不拉地看下来的人没几个。看看我的帖子的字数和你们帖子的字数就能看出来,我对我的想法能够写这么多来解释,你们就可以抛出几个术语然后不闻不问?如果不想讨论就不要来搀和,想讨论就要有诚意。如果只是想证明我如何如何不好,那你们不用证明了,我自己知道自己的缺陷。 NND,没有诚意就不要摆高姿态来教训人。 |
| ||||||||
| 引用:
引用:
引用:
引用:
引用:
引用:
引用:
引用:
………… 也累了。以后再说吧。 |
| ||||
| 1.我不愿意对用户做什么假设或者限制,或者至少要尽量减少这种限制。 2.在新游戏的内存要求都达到1g的现在,在很多电脑都需要使用虚拟内存的现在,内存真的嫌多了吗? 3. 代码:
4.我这么说是因为你前文再拿4G内存来比较,而对于PC上的应用就不能这么比较了。现在还有很多机器用着512M甚至256M的内存。 5.你所说的“统计意义”的统计范围是哪些呢?一个软件类型算一个样本点,还是按照软件数量来算样本点? 6. 悬挂引用:当还有指针指向某块内存时,这块内存就处于“被需要”的状态,这时释放它就会出问题,这是“过早释放”的问题。 内存泄漏:这是“过晚释放”的问题,毕竟现在大多数操作系统都能回收进程的内存资源,所以没多少内存是永远不会回收的,只不过一般回收得太晚而已。 生命期管理,就是在“合适”的时间执行“分配”和“释放”这两个概念所对应的行为。生命期管理机制的责任在确保在“合适”的时间来调用指定的行为,而具体的分配和释放行为由这个机制的使用者来制定。这里的“合适”的时间,就是要使实际的“分配”和“释放”时间尽量紧密地包裹资源的理想生命期。实现了这样的生命期管理机制之后,无论是内存管理还是其他资源管理,其难点在哪?这样的机制是否就是对应着最麻烦的问题? 7.我那个“彻底”针对的是“如果可以彻底解决内存管理问题,就算管理其他资源的时候麻烦一点,也值了。”这句话。我自己没有认为“有技术可以“彻底”解决内存管理问题”。 8. “如果可以彻底解决内存管理问题,就算管理其他资源的时候麻烦一点,也值了。” “内存访问违例可以直接让你这个进程完蛋,其他资源访问出错都没有这么猛的效果。至于你说“而如果其他资源的使用有错,那么可能会比内存访问出错的问题更加隐蔽”,你倒是举一个例子出来?什么错误能够比往内存中写数据的时候写到相邻的另外一个对象里去这种错误更隐蔽,更难重现?” 我那些例子针对的是这几句话。特别想说明的就是的确有些非内存资源的错误会导致比“进程完蛋”更严重的后果,比如用户宁愿经常重启软件也不想自己的数据被破坏(如果不得不在两者间选择的话)。 至于网络连接,比如说网络模块封装了绝大多数的网络操作,而为了封装,网络模块不会把内部的Connection对象的地址泄露到模块外去,因此传出去的都是ConnectionID(一个int),内部用一个hash表映射到Connection对象。而这些ID也是需要管理的,如果管理出错,就会产生错误的映射,而把针对一个连接的操作应用到另一个不相干的连接上。 这种模式在管理其它东西时都可能用到(为了模块的封装性),这里的Connection可能会变成任何其他资源:文件,数据库接口,mutex,硬件的访问接口,或者其他东西。 而“读进来的数据包怎么处理,会不会弄乱”的确属于网络连接管理的范畴,至少在我这里是的,因为我希望网络模块把网络操作尽量封装好,外部使用时不需要针对“网络”这个概念进行太多针对性的操作。所以我的网络模块与上层之间传递的都是消息对象,网络模块负责消息的广播、序列化/还原,还负责调用预处理接口(这里相当于有两次process()的Command),预处理里可能会有加密/解密以及压缩/解压缩,最终上层用的时候就像与本地模块通信那样交换消息对象。 |
| ||||
| 引用:
另外想起来,对于很多用户来说,在笔记本上一边开QQ、一边开浏览器、一边放音乐、一边玩大型游戏,而且还开着防火墙、杀毒监控软件,甚至还在开着下载软件……这对于他们仍然算是正常的应用状态,因为他们要一边抽空用qq聊天,一边用浏览器看游戏攻略,一边听音乐,一边玩游戏,一边顺便下载点电影…… 如果这些软件都不控制对资源的消耗,结果就是用户做不了自己想做的事情。更何况很多人的windows不仅没有优化配置,而且还开着很多界面特效、用着华丽的主题…… 此帖于 2008-03-11 02:42 PM 被 sjinny 编辑. |
| |||
| 引用:
回到资源管理,我觉得资源管理话题太大,scenario之间的区别也太大,统一的方法论是不现实的。但是其中确实有类似的,比如有些是有显示的范围的,由一些适合用一个pool, 由一些则可以完全自动化这些现有的技术已经足够用了。给出一个特定的资源管理问题,或者可以直接套上上面的模式,或者就不得不由分析来得出一套自己的管理模式,而这种情况下通用性一般都不大(通用性大的话,会被抽出来做成一个模式)。由一些需求就是很“人性化”的,比如电话线路的资源管理的周期控制,是事件触发的:对方挂电话,或者无人接听达到1分钟,或者出现异常断线等。相信针对上面的情形写个代码来管理很多人都会,要说把它推广到普通情况,比如UI的窗口管理也是事件驱动的,那么剩下来的公共代码其实并不多,价值也不大,反而会有一些对于那种硬抽出来的东西的“学习曲线”,得不偿失。 |
| ||||
| 原来你们不证明是因为你们的观点是“不证自明”的……那我是不是可以理解为,因为你们认同自己的观点,所以你们不需要证明自己的观点;因为你们不认同我的观点,所以我需要证明我的观点?这样的逻辑就不是讨论的态度,是争执的态度。争执就是说服与被说服的关系,讨论是获得新知的过程。 我们寝室里在讨论问题的时候,至少不会有谁说自己的观点是“不证自明”的……哪怕是教科书,里面的观点也不会这么说……在数学里,“不证自明”的公理也是尽量要定理化的…… 希望只是我误解了cat的意思…… 我已经重复了很多次很多次很多次了:各种资源管理的共同点之一就是生命期管理。生命期管理的失误会导致分配和施放的实际执行的时机不合适,过早释放导致野指针,过晚释放导致内存泄漏,而这两个问题恰恰是难点。 我从来都没想要用一种一成不变的办法来完全解决大量不同的问题。但是,虽然没有方法能够完全解决大量不同的问题,但是这不代表不存在一种方法能够解决大量不同问题中的共同的那部分。各种资源的确有所不同,但是生命期管理是共同的内容,也是最难、最关键的内容(不知道是不是“之一”)。 生命期管理并不一定是显式指定生命期边界的管理方法。无论是引用计数还是scope,这都是具体的方法,但它们都是生命期管理。 对于事件驱动,我假设一般都会存在一个机制来把行为绑定到事件上:比如回调,比如消息,比如轮训……总之能够在发生事件时调用我制定的行为。实际上如果只是“挂电话”这种事件,那么由于事件很容易分辨,所以很容易就把资源释放与事件绑定起来。但是对于“最后一个指向该内存块的指针改变了指向”这种事件,就不那么容易分辨:为了分辨这一事件,使得引用计数不得不使用一个额外的数据来记录引用的数目,多线程环境下的问题也是由这个额外的东西(的访问和维护)引起的。 如果你说的“要说把它推广到普通情况”的含义是指“用上面的事件所对应的管理代码来管理UI里的资源”,那我要说我一直强调的我所认为的共同的问题并不包括这些:这些里面主要都是生命期边界上所调用的行为,而不是生命期边界本身的确定和触发机制,而剩下的消息机制则的确是通用的东西。 我想还是一个问题一个问题的来吧: 目前我想了解的是大家对这个问题的看法: 当找到了一个或一组机制,能够比较好地解决生命期管理的问题后,这样的机制对各种资源的管理是否有很大的帮助? 这里的“生命期管理”是指: 能够按照使用者的要求,把使用者所指定的“分配”行为和“释放”行为绑定在被管理对象的生命期边界上。 1.管理机制对使用者所指定的分配行为和释放行为没有太多约束 2.管理机制确保将会按照使用者所描述的理想生命期边界上调用一次且仅一次。 3.管理机制能够提供充足的方法,让使用者选择和组合这些方法,以获得能够覆盖绝大多数生命期模型的表达力,因此这些不同的方法之间应该是尽量正交的。 这里的生命期边界指“生命期开始”或“生命期结束”这样的时机。而使用者将根据自己所能够获得的信息,向管理机制描述被管理对象的生命期模型。这里的被管理对象,可以是内存块,也可以是任意概念(比如对象、ID值、mutex、文件描述符等)。 现有机制的状体: 栈:符合1,接近2(栈对生命期的包裹一般是“比较紧密的”,只不过距离理想的程度还有小小的距离),不符合3(虽然栈能够与其他机制配合,但是其自身的表达力很有限,所以只能作为多种方法中的一个)。 内存池:符合1,接近2(内存池的包裹程度比栈还要松散一点,但是这经常也是因为程序员对生命期掌握的程度比较低,所以才选择内存池而不是栈),不符合3(同栈) 智能指针:符合1,很接近2(如果能把各种事件和条件良好地映射到智能指针的持有和丢弃上,那么生命期包裹还是很紧密的),接近3(表达力比栈和内存池更强,但是对于以特定条件或事件的组合来确定生命期边界的情况没有提供直接的支持,有时可能需要手动用智能指针对象来把事件映射到生命期管理中) gc:不符合1(毕竟gc*经常*是应用线程之外的独立线程,而且collect的时候还需要停止应用线程的工作[至少要暂停对被collect的内存区域的访问],当然更重要的还是时机的不确定性,所以限制会多一些),不符合2(主要的问题是延后释放使得生命期包裹不紧密),接近3(其实这个方面的问题跟智能指针是类似的——某些生命期模型下需要使用者自己做一些从事件到管理的映射)。 如果想讨论资源管理,我建议从以上这个问题开始。 此帖于 2008-03-11 09:30 PM 被 sjinny 编辑. |
| ||||
| 首先,每一道数学题里,你所使用的根据要么得是公理,要么得是定理,而不能是“不证自明”的东西。公理在我看来更像一种假设,即“假设现实是这样的”,而定理则是在这个基础上推导出来的。 讨论的确要有公共基础,但是如果不愿意向别人解释自己所认为的“公共基础”,那就是缺少诚意的表现。更何况很多时候并不是别人不懂你所指的某些东西,而只是你的符号系统与别人不完全一样,需要一个映射的过程。如果讨论时不愿意明确概念,那也是个态度问题。如果讨论时喜欢用自己的理解来定义别人的概念,那就没有交流的心态,而是始终从自己的视角去评判别人而不愿真正去理解别人。 不理解你的符号不等同于不理解你的符号所代表的事物。 |