前言对于python来说,万物皆对象,所有的变量赋值都遵循对象引用机制。程序运行时,需要在内存中开辟一块空间,用于存放运行过程中产生的临时变量;计算完成后,将结果输出到永久存储器中。如果数据量太大,内存空间管理不好,很容易出现OOM(outofmemory),俗称爆内存,程序可能会被操作系统终止。对于服务器来说,内存管理就更重要了,否则很容易造成内存泄露——这里的泄露并??不是说你的内存存在信息安全问题,被恶意程序利用,而是程序本身没有设计好。导致程序无法释放不再使用的内存。-内存泄漏并不是说你的内存在物理上消失了,而是指代码分配了某块内存后,由于设计错误而失去了对这块内存的控制,造成了内存的浪费。也就是说,这块内存是不受gc控制的。计数引用因为python中的一切都是对象,所以你看到的所有变量本质上都是一个指向对象的指针。当一个对象不再被调用的时候,也就是这个对象的引用计数(指针的个数)为0的时候,就意味着这个对象永远不可达了,自然就变成了垃圾,需要回收。可以简单理解为没有变量指向它。importosimportpsutil#显示当前python程序占用的内存大小defshow_memory_info(hint):pid=os.getpid()p=psutil.Process(pid)info=p.memory_full_info()memory=info.uss/1024./1024print({}memoryused:{}MB.format(hint,memory))您可以看到调用了函数func()。创建列表a后,内存使用量迅速增加到433MB:函数调用结束后,内存恢复正常。这是因为在函数内部声明的列表a是一个局部变量。函数返回后,局部变量的引用将被取消;此时列表a引用的对象的引用号为0,Python会进行垃圾回收,所以之前占用的大量内存又回来了。deffunc():show_memory_info(initial)globalaa=[iforiinrange(10000000)]show_memory_info(aftercreated)func()show_memory_info(finished)##########output##########initialmemoryused:48.88671875MBAfteracreatedmemoryuused:433.94921875MBfinishedmemoryuused:433.94921875MB在这段新代码中,globala意味着将a声明为全局变量。那么,即使函数返回后,对列表的引用仍然存在,所以该对象不会被垃圾回收,仍然占用大量内存。同样,如果我们返回生成的列表,在主程序中接收,那么引用依然存在,不会触发垃圾回收,仍然占用大量内存:deffunc():show_memory_info(initial)a=[iforiinderange(10000000)]show_memory_info(aftercreated)returnaa=func()show_memory_info(finished)##########output##########initialmemoryused:47.96484375MB查看一个变量被引用了多少次?通过sys.getrefcount。importsysa=[]#两个引用,一个来自a,一个来自getrefcountprint(sys.getrefcount(a))deffunc(a):#四个引用,a,python的函数调用栈,函数参数,getrefcountprint(sys.getrefcount(a)))func(a)#两个引用,一个来自a,一个来自getrefcount,函数func调用不存在了print(sys.getrefcount(a))##########output##########242如果涉及函数调用,会额外增加两个1.函数栈2.函数调用。从这里可以看出python不再像C一样需要释放内存,但是python也为我们提供了手动释放内存的方法gc.collect()。importgcshow_memory_info(initial)a=[iforiinrange(10000000)]show_memory_info(aftercreated)delagc.collect()show_memory_info(finish)print(a)##########输出##########初始使用内存:48.1015625MB创建后使用内存:434.3828125MB完成使用内存:48.33203125MB------------------------------------------------------------------------NameErrorTraceback(最近calllast)
