CUDD包内存管理问题:自定义常量节点转换后引用计数异常
CUDD自定义节点转换中的引用计数异常与ITE挂起问题
我修改了CUDD包,在DdNode的type联合体中新增字段,支持形如(op x y)的自定义常量节点,同时编写了一个函数将布尔常量节点转换为0/1值的BDD节点。
转换流程和Cudd_addApply类似,但用Ite替代cuddUniqueInter连接返回值——因为返回值的索引可能和原子节点不同(甚至低于当前层级,会触发断言失败)。
现在回收中间结果时出了问题,我的引用计数管理代码如下(已省略NULL检查):
auto n = Cudd_addIthVar(dd, index); Cudd_Ref(n); // T, E are transformations of children res = (T == E) ? T : Cudd_addIte(dd, n, T, E); Cudd_Ref(res); Cudd_RecursiveDeref(dd, n); // remove temp results Cudd_RecursiveDeref(dd, T); Cudd_RecursiveDeref(dd, E); cuddCacheInsert1(dd, op, f, res); Cudd_Deref(res); return (res);
这段代码能通过Cudd_DebugCheck和Cudd_CheckKeys,但部分内部节点引用计数莫名超过100,且ITE操作出现挂起。
问题排查与修复方案
1. 引用计数异常的核心原因
- 缓存插入后的引用错误:
cuddCacheInsert1会自动为res增加引用计数(CUDD缓存机制要求缓存条目持有节点的引用),但你在插入后调用Cudd_Deref(res),会导致返回节点的引用计数被错误减少,后续其他地方引用该节点时,计数会出现异常飙升。 - T/E的释放逻辑错误:当
T == E时直接返回T,但后续调用Cudd_RecursiveDeref(dd, T)会直接释放掉要返回的节点,导致返回值变成悬空指针,后续操作会引发引用计数混乱甚至内存错误。
2. 修复后的代码
auto n = Cudd_addIthVar(dd, index); Cudd_Ref(n); // T, E are transformations of children DdNode *res; if (T == E) { res = T; Cudd_Ref(res); // 为返回值增加引用,避免后续释放影响 Cudd_RecursiveDeref(dd, n); // 不能释放T/E,因为res就是T,释放会导致返回节点被回收 cuddCacheInsert1(dd, op, f, res); Cudd_Deref(res); // 抵消之前的Ref,缓存持有一个引用 return res; } else { res = Cudd_addIte(dd, n, T, E); Cudd_Ref(res); Cudd_RecursiveDeref(dd, n); Cudd_RecursiveDeref(dd, T); Cudd_RecursiveDeref(dd, E); cuddCacheInsert1(dd, op, f, res); Cudd_Deref(res); // 抵消Ref,缓存持有一个引用 return res; }
3. 额外注意事项
- ITE挂起的关联解决:引用计数异常会导致节点被错误回收或重复保留,进而引发CUDD内部哈希表、缓存结构的死锁或无限循环。修复引用计数逻辑后,ITE挂起的问题大概率会同步解决。
- 调试建议:启用CUDD的
CUDD_DEBUG宏,在关键步骤后调用Cudd_CheckRefs检查单个节点的引用计数,定位异常节点的产生路径。
内容的提问来源于stack exchange,提问作者farmerzhang1
相关产品推荐
相关产品推荐

