当前位置: 首页 > 后端技术 > Python

Python程序是否存在内存泄漏

时间:2023-03-25 22:39:56 Python

为什么要写这篇文章?前段时间测试虚拟机在用的时候死机了,然后登录终端弹出oom错误,一个python程序被kill掉了,所以我就按照之前的经验说,是不是内存泄露了?为什么!也就是我说是不是内存泄漏,然后问题就来了。我们的开发同学说“Python内存泄漏?”这个好,接下来我就分析一下Python会不会泄漏内存。什么是内存泄漏?内存泄漏(MemoryLeak)是指程序中已经动态分配的堆内存没有被释放或由于某种原因无法释放,造成系统内存的浪费,导致运行速度变慢等严重后果程序甚至系统崩溃。(摘要:内存泄漏)内存泄漏发生在Python存在内存泄漏的C语言开发的底层模块中。代码使用全局的list、dict或者其他容器,不断的往这些容器中插入对象。但如果使用后忘记删除回收,代码中存在“引用循环”,循环引用的对象定义了__del__方法,就会发生内存泄漏。演示Python的内存泄漏可以说Python的内存管理是由“Python内存管理器”完成的,我们用户不需要关心内存管理。是的,你完全正确!虽然Python程序不可能像C/C++那样“忘记释放”内存,导致没有足够free的MemoryLeak,但它也会遇到“逻辑上的MemoryLeak”。下面这种情况,当默认参数是变量类型时,会出现内存泄漏,如下:deffoo(a=[]):a.append(time.time())returna和的内存泄漏问题请求模块到目前为止都没有得到解决。让我们来演示一下上面的可变参数是如何导致内存泄漏的。代码如下:#!/bin/python3#encoding:utf-8importpsutil,osdefmemory_usage_psutil():#用于返回内存大小,单位MBprocess=psutil.Process(os.getpid())mem=process.memory_info()[0]/float(2**20)returnmemdefget_current_obj(a=[],i=1):a.append([0]*i)returnadefsum_abj(a):returna**2defmain():obj=[]foriinrange(10000):obj=get_current_obj(obj,i)#正常逻辑,我们每次调用get_current_obj后#内存会被释放,但实际好像不是这样的话,内存#一直在增加。if(i%100==0):print(memory_usage_psutil())print("========下面是sum_obj函数调用。========")forjinrange(10000):sum_abj(j)if(j%100==0):print(len(gc.get_objects()))if__name__=='__main__':main()以下是运行结果。通过截图对比发现函数get_current_obj调用后内存并没有释放。可想而知,如果程序是一直在处理请求的后台程序,那你的服务器内存肯定是爆的。其实我们也可以在程序中加入如下代码,查看程序当前跟踪的对象数量,像这样:importgc...defmain():...if(i%100==0):print(memory_usage_psutil())print("跟踪的对象数",len(gc.get_objects()))...if(j%100==0):print(memory_usage_psutil())print("对象数tracked",len(gc.get_objects()))...最终具体执行效果是这样的,可变参数的对象个数一直在增加:如何解决上面的内存泄漏问题23defmain():24obj=[]25foriinrange(10000):26obj=[]27obj=get_current_obj(obj,i)28#正常逻辑,每次调用get_current_obj29#内存都会被释放,但实际情况是事实并非如此,内存是30#一直在增加。31if(i%100==0):32print("内存使用大小",memory_usage_psutil())33print(len(obj[0]))34print("跟踪的对象数量",len(gc.get_objects())35delobj36if__name__=='__main__':37main()如上,我们只需要增加26和35行代码,即用完再循环。总结最后,Python有内存泄漏吗?答案是肯定的,但并不是你想的C代码“忘记回收内存”造成的内存泄漏,而是程序运行过程中“进程占用的内存莫名其妙地不断上升”。进程占用的内存一直在增加,与逻辑预期不符,最终导致内存泄漏。而我们可以通过以下步骤来验证,总结如下:使用gc.get_objects()获取被追踪对象列表,然后使用len函数计算对象个数。函数返回时,函数中创建的所有内容都要删除)最后比较对象个数。如果函数执行后的对象数大于执行前,则存在内存泄漏参考python内存管理memory-leak-in-python-requestsPython官方模块OSmemoryleak注意在一些平台上,包括FreeBSD和macOS,设置环境可能会导致内存泄漏。putenv().Pythonmemoryleak参考系统文档