C++引用计数的智能指针有效回收方法
2008-03-08 21:41:58 来源:WEB开发网 闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鐐劤缂嶅﹪寮婚悢鍏尖拻閻庨潧澹婂Σ顔剧磼閻愵剙鍔ょ紓宥咃躬瀵鎮㈤崗灏栨嫽闁诲酣娼ф竟濠偽i鍓х<闁诡垎鍐f寖闂佺娅曢幑鍥灳閺冨牆绀冩い蹇庣娴滈箖鏌ㄥ┑鍡欏嚬缂併劎绮妵鍕箳鐎n亞浠鹃梺闈涙搐鐎氫即鐛崶顒夋晬婵絾瀵ч幑鍥蓟閻斿摜鐟归柛顭戝枛椤牆顪冮妶搴′簼缂侇喗鎸搁悾鐑藉础閻愬秵妫冮崺鈧い鎺戝瀹撲礁鈹戦悩鎻掝伀缁惧彞绮欓弻娑氫沪閹规劕顥濋梺閫炲苯澧伴柟铏崌閿濈偛鈹戠€n€晠鏌嶆潪鎷屽厡闁汇倕鎳愮槐鎾存媴閸撴彃鍓卞銈嗗灦閻熲晛鐣烽妷褉鍋撻敐搴℃灍闁绘挻娲橀妵鍕箛闂堟稐绨肩紓浣藉煐濮樸劎妲愰幘璇茬闁冲搫鍊婚ˇ鏉库攽椤旂》宸ユい顓炲槻閻g兘骞掗幋鏃€鐎婚梺瑙勬儗閸樺€熲叺婵犵數濮烽弫鍛婃叏椤撱垹纾婚柟鍓х帛閳锋垶銇勯幒鍡椾壕缂備礁顦遍弫濠氱嵁閸℃稒鍊烽柛婵嗗椤旀劕鈹戦悜鍥╃У闁告挻鐟︽穱濠囨嚃閳哄啰锛滈梺褰掑亰閸欏骸鈻撳⿰鍫熺厸閻忕偟纭堕崑鎾诲箛娴e憡鍊梺纭呭亹鐞涖儵鍩€椤掑啫鐨洪柡浣圭墪閳规垿鎮欓弶鎴犱桓闂佸湱枪閹芥粎鍒掗弮鍫熷仺缂佸顕抽敃鍌涚厱闁哄洢鍔岄悘鐘绘煕閹般劌浜惧┑锛勫亼閸婃牠宕濋敃鈧…鍧楀焵椤掍胶绠剧€光偓婵犱線鍋楀┑顔硷龚濞咃絿妲愰幒鎳崇喓鎷犻懠鑸垫毐闂傚倷鑳舵灙婵炲鍏樺顐ゆ嫚瀹割喖娈ㄦ繝鐢靛У绾板秹寮查幓鎺濈唵閻犺櫣灏ㄥ銉р偓瑙勬尭濡繂顫忛搹鍦<婵☆垰鎼~宥囩磽娴i鍔嶉柟绋垮暱閻g兘骞嬮敃鈧粻濠氭偣閸パ冪骇鐎规挸绉撮—鍐Χ閸℃ê闉嶇紓浣割儐閸ㄥ墎绮嬪澶嬪€锋い鎺嶇瀵灝鈹戦埥鍡楃仯闁告鍕洸濡わ絽鍟崐鍨叏濡厧浜鹃悗姘炬嫹

核心提示:引用计数指针是否能有效地回收,对系统意外关机之后数据的恢复来说至关重要,C++引用计数的智能指针有效回收方法,要害是要避免对象复制,怎样从灾难性故障中,较小的内存消耗要归功于软件中使用了上文方法来串行及反串行化不常用的类对象的那些模块,当任务完成时,恢复一个长期运行、系统级的后台守护进程或者服务,在如今的软件设计过程中
引用计数指针是否能有效地回收,对系统意外关机之后数据的恢复来说至关重要,要害是要避免对象复制。
怎样从灾难性故障中,恢复一个长期运行、系统级的后台守护进程或者服务,在如今的软件设计过程中,已成为了一个重要的考虑因素。当这些软件是由C++语言编成,并使用了引用计数的智能指针时,那么,智能指针的有效回收,对系统是否具有可伸缩级的恢复能力、甚至正确地继续未完成的操作来说,都显得至关重要。
在本文中,描述了一种方法,可从关机之后的软件恢复中,有效地回收引用计数指针,而且此方法在内存占用方面也非常高效,这种方法的要害在于避免对象复制,而对象复制通常是由C++中指针引用的串行化与反串行化这种传统技术产生的。当从存档文件中反串行化时,本方法使用了标记(tag)来唯一地识别指针对象,且在系统恢复时由一个对象缓存来保存指针引用。
本文以一个基于事件的商业实时作业调度系统来进行演示,其通常由大型市场咨询公司使用,天天都会在集群工作站上处理数不胜数的计算任务。
为什么许多C++软件项目会使用自动内存治理技术呢,因为它有以下好处:
² 代码安全性。避免了太早释放一个对象所带来的风险。
² 代码正确性。避免了忘记释放未使用内存所带来的风险。
² 代码模块性。代码中不再需要点缀着与程序无关的簿记代码。
² 编程简单性。现在可假定一种无限内存的计算模式。
² 编程高效性。程序员不再担心内存治理问题。
引用计数智能指针,有时也称为“计数体术语”,是一种生命期受管的对象,其对引用它的数量,有一个内部的计数器。当内部引用计数为零时,这些对象会自动销毁自身,这是一种非常有用的技术,已运用在许多C++软件产品项目中,因为简单易行,且无需对语言或编译器进行任何扩展。
引用计数智能指针能进一步定义为一体式或分离式,一体式智能指针把引用计数放在自身内,而分离式智能指针则把引用计数放在对象之外。在本文中,使用的是分离式智能指针方案,这需要在访问实际对象指针之前,在智能指针模板对象中重载 -> 或 * 操作符,从本质上来说,这也是代理(PRoxy)设计模式的一个特例。
就目前来说,还没有一种方案以高效利用内存的方式描述了怎样恢复智能指针,而传统的C++对象串行与反串行化方法,会导致内存低效,因为当一个反串行化的对象碰到一个对它的引用时,总是会创建一个新对象,在最坏的情况下,这会把一个恢复后的守护进程的内存消耗量,推到一个无法接受的高度,致使它无法继续运行下去。
问题的引出
传统对象的串行与反串行化方案,也能实现智能指针,只不过在内存上比较低效而已。在这些传统方案中,当一个对象串行化时,对象内的成员指针被解引用,它的内容与对象一起“串行”进存档文件中。这种方法的问题在于,当反串行化时,成员指针会再次构造,且是每个恢复的对象都会这样。
下面以基于事件的作业调度系统来进行讲解,作业定义在CJobDef对象中,其包含了作业的静态属性,如它执行的命令、工作目录、及作业执行时的用户ID。而作业定义的运行实例则包装在CJobInst对象中,其包含了一些与实例有关的属性,如它的进程ID、执行参数、及运行历史记录。在类层次上,每个CJobInst对象都包含了一个成员,其引用到触发这次作业实例的原始CJobDef对象。
图1是软件停止运行之前的系统,运行时CJobInst对象的多个实例可能会引用至同一个CJobDef对象。在软件停止及恢复后,传统串行化对象恢复方法,会导致为每个运行的CJobInst对象,都创建一个CJobDef对象,如图2中所示。


以下是引用片段: class CJobDef { friend CArchive & Operator >> (CArchive &ar, CJobDef *def) { ar >> def->command; } friend CArchive & operator << (CArchive &ar, CjobDef *def) { ar << def->command; } private: std::string command; }; class CJobInst { friend CArchive & operator >> (CArchive &ar, CJobInst *inst) { inst->m_def = new CJobDef; ar >> inst->m_def; } friend CArchive & operator << (CArchive &ar, const CJobInst *inst) { ar << inst->m_def; } private: CJobDef *m_def; }; |
以下是引用片段: typedef Ref<CJobDef> CJobDefPtr; |

以下是引用片段: class CJobDef : public CReferable { friend CArchive &operator << (CArchive &ar, const CJobDefPtr &cand) { ar << cand->getTag(); CArchive ar_def(cand->getTag(), CArchive::WRITE); // write object attributes to ar_def return ar; } friend CArchive &operator >> (CArchive &ar, CJobDefPtr &cand) { int tag; ar >> tag; CArchive ar_def(tag, CArchive::READ); // read object attributes from ar_def cand.impersonate(tag); return ar; } }; class CJobInst : public CReferable { friend CArchive & operator << ( CArchive &ar, const CJobInstPtr &cand) { ar << cand->m_defPtr; return ar; } friend CArchive & operator >> (CArchive &ar, CJobInstPtr &cand) { CJobDefPtr defPtr = new CJobDef; ar >> defPtr; cand->m_defPtr = defPtr; return ar; } }; |

以下是引用片段: CJobDefPtr jobDefPtr = new CJobDef |
运行任务数 | 软件重启前的内存占用大小 | 软件重启后的内存占用大小 |
5000 | 25M | 32M |
100000 | 370M | 413M |
200000 | 730M | 795M |
更多精彩
赞助商链接