python 内存管理 内存泄漏及排查方案 内存友好的python代码
Python 内存管理一、一句话总结Python 的内存管理就是三件事自动分配内存你不用管变量存在哪自动回收垃圾不用的对象自动删掉靠引用计数 分代垃圾回收实现二、核心机制 1引用计数最基础是什么每个对象都有一个引用计数器记录有多少个变量指向它。规则变量指向它 →计数 1变量不再指向它 →计数 -1计数 0→立刻被回收内存释放例子a[1,2,3]# 引用计数 1ba# 引用计数 2dela# 引用计数 1delb# 引用计数 0 → 立即回收优点实时性高不用的对象马上释放内存缺点无法解决循环引用这是最大弱点三、核心机制 2垃圾回收GC解决循环引用什么是循环引用a[]b[]a.append(b)b.append(a)a 引用 bb 引用 a → 互相引用谁也不释放。引用计数永远不会变成 0 → 内存泄漏所以 Python 引入GC垃圾回收专门清理这种情况。四、核心机制 3分代回收GC 工作方式Python 把对象分成 3 代0 代新创建的对象扫描最频繁1 代活过一次GC2 代活得很久的对象扫描最少规则活得越久越不可能被删越新的对象越容易死越要频繁检查效率极高五、内存池机制提升速度Python 不会每次都向操作系统要内存而是自己维护一个内存池小块内存 → 从内存池拿用完归还内存池避免频繁申请/释放速度快很多六、完整流程面试必背创建对象 → 自动分配内存用引用计数跟踪对象被使用次数计数0 →立即释放遇到循环引用→GC分代回收清理小内存使用内存池提升效率七、一句话总结Python 内存管理以引用计数为基础负责实时回收对象通过分代垃圾回收解决循环引用问题并使用内存池减少内存开销提升效率。Python 内存泄漏 排查方案面试实战一、先懂什么是内存泄漏本该被销毁的对象引用一直没断开GC回收不掉内存只涨不跌常见原因循环引用没断干净全局变量一直持有大对象长连接/缓存无限堆积不清空线程、定时器、事件监听忘记关闭第三方库/对象手动内存不释放二、最常见 3 种泄漏场景1. 循环引用引用计数失效a[]b[]a.append(b)b.append(a)# 互相引用计数永远不为0老版本极易泄漏现在Python3 GC能自动清理但大量批量循环引用依旧会卡、占内存2. 全局变量常驻内存cache_list[]# 全局列表defadd_data():foriinrange(100000):cache_list.append([i]*100)# 无限塞永不清空程序运行越久内存越大3. 类实例/闭包持续持有引用deffunc():big_datalist(range(1000000))definner():print(len(big_data))returninner ffunc()# inner闭包一直持有big_data无法释放三、4 大排查工具由简到难1. 内置模块 gc 查看回收情况importgc# 开启调试打印回收详情gc.set_debug(gc.DEBUG_LEAK)# 手动触发垃圾回收gc.collect()# 查看未被回收的泄漏对象print(gc.garbage)gc.garbage里有内容 确定存在泄漏对象2. psutil 实时监控进程内存importpsutilimportosdefget_memory():pidos.getpid()ppsutil.Process(pid)# 单位 MBreturnp.memory_info().rss/1024/1024print(get_memory())循环运行代码观察内存持续上涨不回落泄漏实锤3. objgraph 定位泄漏对象最常用安装pipinstallobjgraph用法importobjgraph# 查看数量最多的对象objgraph.show_most_common_types(limit20)# 查看某个对象的引用链找到是谁还在持有objgraph.show_backrefs(objgraph.find_backref_chain(泄漏对象))直接追踪谁在死死抓住这个对象不放4. memory_profiler 逐行分析内存安装pipinstallmemory-profiler用法frommemory_profilerimportprofileprofiledeftest():# 你的业务代码passtest()运行后逐行显示每行占用内存精准定位爆内存代码四、解决内存泄漏 6 个实用方案主动断开引用不用对象手动赋值Nonebig_dataNone用完及时 del 删除dela,b清空全局缓存/列表cache_list.clear()避免闭包长期持有大对象用完主动解绑手动触发GC定时执行importgc gc.collect()改用弱引用 weakref不想强持有用弱引用不影响回收importweakref objweakref.proxy(原对象)五、面试标准回答Python内存泄漏大多由循环引用、全局常驻对象、闭包强引用、资源未释放导致排查可使用gc模块查看垃圾对象、psutil监控进程内存、objgraph追踪引用链、memory_profiler逐行分析解决方式主动置空引用、及时删除对象、定期清理缓存、使用弱引用、手动触发垃圾回收。六、速记口诀内存只涨不回落大概率是引用锁循环引用全局存闭包持有甩不脱gc查垃圾psutil看涨跌objgraph追源头。如何写出内存友好的 Python 代码谨慎使用全局变量 全局变量通常会进入 GC 的第 2 代长期霸占内存。能放在函数内部的局部变量就不要放在全局。避免死结般的循环引用 如果在设计树状结构或图结构时不可避免地遇到双向引用比如父节点指向子节点子节点又指向父节点请使用标准库中的 weakref弱引用模块。弱引用不会增加对象的引用计数。处理海量数据时使用生成器 如上一篇文章所说生成器按需产出数据不会一次性撑爆内存池。手动触发 GC必要时 在跑完一个极消耗内存的数据清洗脚本后可以通过 import gc; gc.collect() 强制执行一次全量垃圾回收清理内存碎片。深入底层才能在架构和性能优化时游刃有余。Python 绝不仅仅是简单的脚本语言它的内部设计同样充满了权衡与智慧。一、核心原则少创建、晚创建、及时释放、按需加载、避免常驻二、变量与对象优化不用就置空 / del 销毁# 用完大对象立刻解绑large_dataNonedellarge_data优先局部变量少用全局变量全局变量生命周期贯穿整个程序长期占内存局部函数执行完自动释放。避免无意识创建临时大对象# 差生成完整新列表new_list[x*2forxinbig_list]# 好用生成器不占整块内存gen(x*2forxinbig_list)小数据复用对象减少频繁新建循环内不要反复创建大容器提前初始化复用。三、容器选型最关键海量遍历优先用 生成器 / 迭代器列表一次性载入全量数据生成器边用边生成几乎不占内存# 占内存numslist(range(10000000))# 内存友好numsrange(10000000)大量键值存储按需选结构普通字典够用就不用嵌套多层字典固定结构数据用namedtuple/dataclass更省内存数值矩阵优先numpy 数组比原生列表省超多内存大列表及时清空data_list.clear()四、循环写法优化避免循环内嵌套循环生成大中间数据遍历优先 for-in 迭代不要先切片复制# 浪费复制一份新列表foriteminarr[:]:pass# 友好直接遍历原数组foriteminarr:pass分批处理大数据不要一次性全读入文件、日志、数据库数据分页/分块读取而非一次性加载到内存。五、字符串优化大量拼接不用频繁字符串拼接不断新建对象改用列表收集最后join# 差sforiinrange(1000):sstr(i)# 优tmp[]foriinrange(1000):tmp.append(str(i))s.join(tmp)六、资源与IO内存优化文件、网络连接、数据库连接用完必关闭优先with上下文管理器自动释放资源withopen(test.txt,r,encodingutf-8)asf:textf.readline()# 出作用域自动关闭释放日志、缓存设置上限自动淘汰缓存字典/列表加最大长度超出自动丢弃旧数据防止无限膨胀。七、面向对象写法优化类属性少存冗余数据实例只存必要字段冗余计算属性用property动态计算不常驻内存慎用闭包长期捕获大对象闭包会强引用外层大变量导致无法GC回收需要弱关联用 weakref 弱引用不想阻碍垃圾回收使用弱引用不增加引用计数八、GC 与内存回收优化批量处理完数据后手动触发垃圾回收importgc gc.collect()减少循环引用代码写法从源头规避内存泄漏九、第三方库内存技巧Pandas读取csv指定usecols只读取需要列调低数值类型int64→int32、float64→float32图像处理处理完图像数组及时销毁不要全局常驻大图矩阵十、极简背诵总结面试直接说多用生成器迭代器替代列表分批加载大数据优先局部变量严控全局大对象常驻内存资源使用with自动释放用完及时置空删除对象字符串拼接用列表join避免频繁新建字符串精简类实例属性减少冗余存储必要时使用弱引用大数据分页分块处理杜绝一次性全量载入内存。内存友好代码 VS 耗内存代码 对照表一、大数据遍历耗内存写法内存友好写法优势data list(range(10000000))data range(10000000)不生成完整列表迭代取值几乎无内存占用列表推导式[x*2 for x in arr]生成器表达式(x*2 for x in arr)边遍历边计算不存全部结果二、变量作用域耗内存友好写法全局定义大列表/大字典放到函数内作为局部变量长期保留无用大对象del obj/obj None三、循环遍历低效写法优化写法for i in arr[:]:切片复制for i in arr:直接遍历循环内频繁创建空列表外部提前创建循环内清空复用四、字符串拼接浪费内存高效写法s str(i)循环累加列表收集 .join()五、文件读取耗内存友好写法f.read()一次性读整文件for line in f:逐行读取不手动关闭文件with open() as f:上下文管理器六、容器存储笨重写法轻量化写法多层嵌套字典存结构化数据namedtuple/dataclass纯Python列表存海量数值numpy.array统一存储列表无限追加不限制设置最大长度自动清理旧数据七、数据处理错误写法正确写法一次性读取全量数据库数据分页查询、分批拉取图像处理后全局保存原图用完立即销毁图像数组八、引用优化容易泄漏优化方案闭包捕获超大外层变量执行完毕手动解绑变量大量对象强引用绑定weakref弱引用九、垃圾回收放任不管主动优化依赖系统自动GC大批量数据处理后gc.collect()大量循环引用代码重构逻辑减少互相引用十、Pandas专属优化高内存省内存读取所有列usecols指定只读取需要列默认int64/float64向下转型 int32、float32通用极简守则能迭代不存全量能局部不全局能复用不新建能清空不堆积资源用完必释放大对象用完必解绑分批处理大数据拒绝一次性塞满内存
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2633929.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!