网易首页 > 网易号 > 正文 申请入驻

pandas 3.0 内存调试指南:学会区分真假内存泄漏

0
分享至

你有没有遇到过,在使用pandas的时候批处理任务跑完了,del df 执行了,甚至还使用了 import gc; gc.collect() 但是进程内存确没有减少。

我们首先就会想到这可能是"pandas 有内存泄漏",其实这不一定就是泄漏。可能是引用、分配器的正常行为。而且在pandas 3.0 之后这类情况更多了,因为Copy-on-Write 改变了数据共享的方式,Arrow 支持的 dtype 让内存行为变得更难预测。



RSS 不是"正在使用的内存"

很多人把 RSS 当成实际内存占用来看,这是问题的根源。

RSS 是操作系统报告的常驻内存大小,而Python 对象实际需要多少内存是另一回事。分配器为了提高效率会预留一大块内存池(arena)以备后用。删掉一个 DataFrame,Python 层面的对象确实释放了但 RSS 不一定下降,因为分配器(Python 的、NumPy 的、Arrow 的、libc 的)只是把这块内存标记为"可重用",并没有还给操作系统。

这就解释了一个常见现象:监控面板上看着像在泄漏,但程序跑得好好的,吞吐量很稳定。内存在进程内部被重复利用,RSS 高位运行其实是正常的。

Copy-on-Write 带来的认知陷阱

pandas 3.0 默认启用了 Copy-on-Write。从用户角度看索引操作和很多方法都"像是"返回了副本,不用再担心意外修改原数据。听起来很好,但这里有个容易忽略的点:CoW 改善的是行为安全性,跟内存什么时候释放没有直接关系。

底层实现上,CoW 会让多个 DataFrame 或 Series 共享同一块数据缓冲区,直到某个对象发生写操作才触发真正的复制。换句话说,你以为创建了好几个独立的副本,实际上它们可能都指向同一块内存。只要任意一个派生对象还活着,这块内存就不会被释放。

哪删掉了"主" DataFrame?没用的,如果某个 Series 切片还在作用域里那一大块缓冲区照样活得好好的。

最常见的"假泄漏":视图比主对象活得久

import pandas as pd
df = pd.DataFrame({"a": range(10_000_000), "b": range(10_000_000)})
view = df[["a"]] # looks small, but can keep df's blocks alive
del df # you expect memory drop
# view still references the underlying data, so buffers can remain

这是实际使用的时候碰到最多的情况。一个看起来人畜无害的 view,实际上在底层持有整个大表的数据块引用。你删掉了 df,但 view 没删内存就这么留着了。

那些不是"副本"的"副本"

即便不考虑 CoW,pandas 本身就有很多这类行为:操作返回的对象可能共享底层数据块,或者内部维护着某些引用。而Python 变量只是冰山一角。闭包、缓存字典、全局变量、异步任务,这些任何一个都可能悄悄地让对象存活下去。

几个高频踩坑场景:

把中间结果存进列表"方便调试":

snapshots = []
for chunk in chunks:
df = transform(chunk)
snapshots.append(df) # you keep every chunk alive

每个 chunk 都活着,内存持续增长。

按用户 ID 或任务 ID 缓存结果,开发阶段觉得挺聪明,上了生产变成了内存博物馆——只进不出。

还有一种是 GroupBy 加上一长串 apply 链式调用,中间产生大量临时对象,GC 来不及回收,尤其在循环里更明显。

Arrow buffers:快是真快,粘也是真粘

pandas 3.0 默认启用了专用的 string dtype,装了 PyArrow 的话字符串列会用 Arrow 作为底层存储。性能和内存效率都有提升,但代价是内存行为变得更复杂。

Arrow 有自己的缓冲区管理和内存池机制。你可能会看到这种诡异的现象:pyarrow.total_allocated_bytes() 显示 Arrow 那边已经释放得差不多了,但 psutil.Process().memory_info().rss 却一直往上涨。

这不一定是泄漏,更可能是内存池化加上碎片化加上延迟释放的综合效果。

双缓冲区

从 Parquet 读数据是很常见的操作。先读成 Arrow Table,再转成 pandas DataFrame,如果两个对象都留在作用域里,等于同一份数据在内存中存了两遍。

import pyarrow.parquet as pq
table = pq.read_table("big.parquet")
df = table.to_pandas() # now you may hold Arrow buffers + pandas objects
# If table stays referenced, memory won't drop as you expect

解决方法也很简单,转换完就 del 掉源对象。

排查检查清单

与其凭直觉猜测,不如系统地排查。

第一步,确认到底是持续增长还是一次性的高水位。同一个进程里把任务跑两遍,如果第一遍 RSS 上升、第二遍稳定,那多半是分配器在重用内存,不是泄漏。如果 RSS 随着工作量线性增长,那确实有东西在不断积累——可能是真正的泄漏,也可能是某个无限增长的缓存。

第二步,关注对象引用而不是内存数字。用 gc.get_objects() 采样观察对象数量变化趋势,用 tracemalloc 追踪 Python 层面的分配模式,用 objgraph 找出哪些类型在增长、被谁持有。

第三步,区分 Python 堆和原生缓冲区。Python 分配可以用 tracemalloc 和 pympler 看,进程 RSS 用 psutil,Arrow 的内存用 pyarrow.total_allocated_bytes()。如果 Python 层面很平稳但 RSS 在涨,问题多半出在原生内存池或碎片上。

第四步,排查意外引用。DataFrame 或 Series 有没有被存进全局变量、类属性或者某个缓存字典?有没有往列表里追加数据忘了清理?lambda 或回调函数有没有闭包了 df?有没有返回的对象内部持有大对象的引用?

第五步,实在搞不定就用进程隔离。跑 Arrow/Parquet 密集型任务时,把工作放到 worker 进程里,定期回收 worker(比如每处理 N 个文件就重启一次),让操作系统来当垃圾收集器。

总结

pandas 的"内存泄漏"多数时候是下面几种情况:视图或切片持有大缓冲区的引用导致无法释放;Copy-on-Write 机制让数据共享的时间比预想的长;Arrow 或其他原生分配器即使对象释放后仍保留内存池;缓存、列表、闭包、长期任务导致对象被意外持有。

真正有效的应对方式不是 gc.collect(),而是:缩短对象生命周期,避免无意间保留引用,测量正确的指标,必要时用进程回收来兜底。

https://avoid.overfit.cn/post/44a0a3f2e4544cbe9307e9afe262779b

by Nikulsinh Rajput

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
乔治今日命中9记三分,追平76人队史纪录

乔治今日命中9记三分,追平76人队史纪录

懂球帝
2026-01-28 14:06:06
植物人男孩,醒来后的第一句话竟然是:医护人员虐待、性侵我!

植物人男孩,醒来后的第一句话竟然是:医护人员虐待、性侵我!

有书
2026-01-27 21:25:03
越南政坛大换血:没有战争记忆的一代,会如何与中国打交道

越南政坛大换血:没有战争记忆的一代,会如何与中国打交道

信马游
2026-01-27 21:42:05
香港魔幻骗局:骗子租豪宅立人设,洪金宝江美仪等明星,被骗数亿

香港魔幻骗局:骗子租豪宅立人设,洪金宝江美仪等明星,被骗数亿

不写散文诗
2025-12-23 20:24:32
章泽天父母离婚内幕曝光!家里有五六个工人,还有个同父异母妹妹

章泽天父母离婚内幕曝光!家里有五六个工人,还有个同父异母妹妹

李健政观察
2026-01-28 14:21:41
一支笔锁死中国14亿人命运:为何90年过去了,这条斜线谁也动不了

一支笔锁死中国14亿人命运:为何90年过去了,这条斜线谁也动不了

半解智士
2026-01-20 18:03:00
纽约期银涨幅扩大至8%

纽约期银涨幅扩大至8%

每日经济新闻
2026-01-28 09:15:05
A股:周四稳了!盘面突发“王炸信号”,这几个板块将直接起飞

A股:周四稳了!盘面突发“王炸信号”,这几个板块将直接起飞

夜深爱杂谈
2026-01-28 18:02:28
朝鲜为库尔斯克阵亡士兵建国葬级纪念馆,1.4万远征军半数伤亡

朝鲜为库尔斯克阵亡士兵建国葬级纪念馆,1.4万远征军半数伤亡

老马拉车莫少装
2026-01-28 21:07:39
两大AI龙头爆了!“卖铲人”工业富联预计净利超350亿,AI服务器卖爆;科大讯飞“翻身”,四季度大赚近10亿

两大AI龙头爆了!“卖铲人”工业富联预计净利超350亿,AI服务器卖爆;科大讯飞“翻身”,四季度大赚近10亿

金融界
2026-01-28 18:50:22
再休一场将无缘评奖!詹姆斯呼吁NBA改规则:如今比赛强度不一样

再休一场将无缘评奖!詹姆斯呼吁NBA改规则:如今比赛强度不一样

罗说NBA
2026-01-28 05:52:56
Here we go!罗马诺:弗拉门戈以创纪录的4200万欧签回帕奎塔

Here we go!罗马诺:弗拉门戈以创纪录的4200万欧签回帕奎塔

懂球帝
2026-01-28 21:06:31
83岁卢海鹏开演唱会,上台颤颤巍巍要人扶,和小8岁陈百祥像父子

83岁卢海鹏开演唱会,上台颤颤巍巍要人扶,和小8岁陈百祥像父子

一娱三分地
2026-01-28 18:00:24
郑丽文好猛,送高市早苗一记重拳!大陆决定:给国民党一个大面子

郑丽文好猛,送高市早苗一记重拳!大陆决定:给国民党一个大面子

爱下厨的阿酾
2026-01-28 18:24:36
取消统考,深圳多区小学期末卷掀热议!有人称堪比公务员行测

取消统考,深圳多区小学期末卷掀热议!有人称堪比公务员行测

南方都市报
2026-01-28 19:19:11
“前湖南卫视主持人李湘再爆惊天大瓜!”

“前湖南卫视主持人李湘再爆惊天大瓜!”

每日一见
2026-01-28 11:58:35
金·卡戴珊女儿12岁小西北直播露黑牙饰,早熟身材,星二代太疯狂?

金·卡戴珊女儿12岁小西北直播露黑牙饰,早熟身材,星二代太疯狂?

娱乐领航家
2026-01-28 21:30:03
需驾驶员监督的特斯拉FSD系统下月在中国获批?知情人士:消息不实

需驾驶员监督的特斯拉FSD系统下月在中国获批?知情人士:消息不实

第一财经资讯
2026-01-23 14:07:58
全都“叛变”了,台积电、三星接连宣布,外媒:中国不买了?

全都“叛变”了,台积电、三星接连宣布,外媒:中国不买了?

古事寻踪记
2026-01-28 07:10:41
妩媚:一副天生自带的滤镜

妩媚:一副天生自带的滤镜

疾跑的小蜗牛
2026-01-28 20:51:13
2026-01-28 22:20:50
deephub incentive-icons
deephub
CV NLP和数据挖掘知识
1904文章数 1445关注度
往期回顾 全部

科技要闻

它是神也是毒!Clawdbot改名卷入千万诈骗

头条要闻

单价6.5万的新房小区出现1431条裂缝 业主:边补边裂

头条要闻

单价6.5万的新房小区出现1431条裂缝 业主:边补边裂

体育要闻

没天赋的CBA第一小前锋,秘诀只有一个字

娱乐要闻

金子涵拉黑蔡徐坤,蔡徐坤工作室回应

财经要闻

从万科退休20天后,郁亮疑似失联

汽车要闻

新手必看!冰雪路面不敢开?记住这4点 关键时刻真能保命

态度原创

房产
本地
时尚
公开课
军事航空

房产要闻

实景兑现在即!绿城,在海棠湾重新定义终极旅居想象!

本地新闻

云游中国|拨开云雾,巫山每帧都是航拍大片

开衫,是春天最好穿的衣服

公开课

李玫瑾:为什么性格比能力更重要?

军事要闻

伊朗竖起巨幅宣传画:一艘美军航母被炸

无障碍浏览 进入关怀版