Python中如何释放外部C库(如libgap)分配的内存?
问题:Python调用外部C库导致内存无法释放
背景
我基于Python 3.12和Sagemath开发的项目内存占用过高,调试后发现:
- tracemalloc显示Python代码本身仅占用约300MB
resource.getrusage显示总占用2300MB,WSL任务管理器显示6GB(受虚拟机机制影响)- 两者的2GB差值来自Sagemath调用的外部C库(如libgap),且这些内存是冗余的——我仅保留了转换为
frozenset的结果,原C库创建的组对象并未留存
代码场景
核心逻辑如下:
for r in range(1, n): # 初始化变量、加载引用 # 导入tracemalloc、resource,定义内存显示函数 G = self.groups[r] tracemalloc.start() for N, word in enumerate(huge_data_set): if N % threshold == 0: display_memory_usage() # 生成生成元 generators = ... stab = frozenset(G.subgroup(generators)) self.stabiliser_lookup.update({(word, otherdata) : (stab, moredata)}) # 类似操作,偶尔调用Sage的群创建/群元素创建函数 perm_indices = ... self.permutation_lookup.update({(word, otherdata) : G(perm_indices)}) # 省略更多嵌套循环
tracemalloc显示:我的代码最高行仅占30MB,Sage内部代码最多80MB,远低于总占用的2.3GB。我确认不需要C库创建的中间对象,但这些内存无法自动释放,输入规模稍大就会导致WSL崩溃。
需求
如何释放这部分外部C库占用的额外2GB内存?理想方案是明确缓存所需数据,释放所有无关引用,但不清楚具体实现方式。
解决方案
针对Sagemath调用libgap等C库导致的内存无法释放问题,可按以下方式处理:
1. 显式清理Sage/GAP对象引用
Sage的群对象(比如G.subgroup(generators)返回的实例)内部持有GAP C库的内存引用,即便转成frozenset,原对象可能仍被隐式引用:
- 转换完成后显式删除原对象,并触发垃圾回收:
subgroup_obj = G.subgroup(generators) stab = frozenset(subgroup_obj) del subgroup_obj # 删除原群对象 import gc gc.collect() # 触发Python垃圾回收,促使Sage释放底层C内存 - 清理GAP的全局对象缓存:
import sage.libs.gap.libgap as libgap libgap.clear_cache() # 清除GAP内部缓存的未使用对象
2. 用子进程隔离C库调用
如果单进程内的清理无效,可将调用C库的逻辑封装到子进程中,利用操作系统进程销毁机制彻底释放内存:
from multiprocessing import Pool def process_batch(batch_data, group): result = {} for word, otherdata, perm_indices in batch_data: generators = ... subgroup_obj = group.subgroup(generators) stab = frozenset(subgroup_obj) result[(word, otherdata)] = (stab, moredata, group(perm_indices)) return result # 主循环分批处理 batch_size = 100 # 根据内存情况调整批次大小 for r in range(1, n): G = self.groups[r] batches = [huge_data_set[i:i+batch_size] for i in range(0, len(huge_data_set), batch_size)] with Pool(processes=1) as pool: for batch in batches: batch_result = pool.apply(process_batch, args=(batch, G)) # 合并结果到全局字典 self.stabiliser_lookup.update({k: (v[0], v[1]) for k, v in batch_result.items()}) self.permutation_lookup.update({k: v[2] for k, v in batch_result.items()})
子进程结束后,操作系统会自动回收其所有内存(包括C库占用的部分)。
3. 调整Sage/GAP的内存管理参数
- 设置GAP的内存使用上限:
import sage.libs.gap.libgap as libgap libgap.set("MemoryUsageLimit", 512) # 单位为MB,限制GAP的内存占用 - 临时禁用Sage的对象缓存(注意会影响性能,需测试权衡):
sage.misc.cachefunc.CacheFunc._cache_enabled = False
4. 调用C库原生清理命令
直接调用GAP的内存清理命令,释放未使用的内存:
import sage.libs.gap.libgap as libgap libgap.eval("CleanWorkspace();") # 执行GAP的工作区清理命令
内容的提问来源于stack exchange,提问作者FShrike
相关产品推荐
相关产品推荐

