在Python程序运行的时候,会在内存中开辟一块空间,用于存放临时变量;当计算完成之后,就会将结果输出到永久性存储器中。如果数据量特别大,那内存空间管理不妥当的话就非常容易爆内存,程序可能直接终止。
在Python中,一切皆对象。所以,每一个变量,实际上都是对象的一个指针。所以,当这个对象的引用计数(指针数)为0的时候,说明它也变成了垃圾,需要被放到回收箱中。
- 当数据是局部变量时,在返回到函数调用处时,局部变量的引用会注销。这时,列表a所指代对象的引用数为0,Python便会执行垃圾回收,因此之前占用的内存被收回了。
- 当数据是全局变量的时,即使函数体内代码执行完毕,返回到函数调用处时,对列表a的引用仍然是存在的,所以对象不会被垃圾回收,依然占有大量内存。
- 再有就是,如果一个数据被引用,在其被引用完成时,内存依旧占用
- 引用机制:
import sys
a = [1,2,3]
print(sys.getrefcount(a)) #2 定义算一次 调用getrefcount算一次
#ref 引用 获得引用次数
print(sys.getrefcount(a)) #2 调用2次getrefcount也只算1次,调用多少次都只算一次
b = a
c = b
print(sys.getrefcount(a)) #4 因为b c也引用了a,所以这也会算作a的引用次数
以上为变量的引用次数计算机制
当一个变量的引用次数为0时,就达到了垃圾回收的充分条件
- 手动回收垃圾:
- 如果我们可以手动删除完对象的引用,然后强制调用gc.collect()清除没有引用的对象,其实也就是手动的启动对象的回收。
- 但是面对两个互相引用的对象,这两者的内存始终无法释放,两者的引用数量始终不为0,所以也达不到清理的条件。
- 此时就需要用到手动垃圾处理
- gc.collect
import os
import psutil
def show_info(start):
pid = os.getpid()
p = psutil.Process(pid)
info = p.memory_full_info()
memory = info.uss/1024/1024
print(f'{start} 一共占用{memory:.2f}MB')
def func():
show_info('initial')
a = [i for i in range(100000)]
b = [i for i in range(100000)]
show_info('created')
a.append(b)
b.append(a)
func()
show_info('finished')
运行结果:
initial 一共占用6.71MB
created 一共占用15.30MB
finished 一共占用15.30MB
此时内存并未释放,此时需要手动释放啦
import os
import psutil
import gc
def show_info(start):
pid = os.getpid()
p = psutil.Process(pid)
info = p.memory_full_info()
memory = info.uss/1024/1024
print(f'{start} 一共占用{memory:.2f}MB')
def func():
show_info('initial')
a = [i for i in range(100000)]
b = [i for i in range(100000)]
show_info('created')
a.append(b)
b.append(a)
func()
gc.collect()
show_info('finished')
- 加入的内容为gc.collect()
-运行结果:
initial 一共占用6.72MB
created 一共占用15.17MB
finished 一共占用7.51MB
此时已经清理内存
不过,尽管我们会进行手动垃圾处理,但是一定情况下还是会出现内存泄漏的情况。内存泄漏:内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
此类情况的原因:
第一是对象被另一个生命周期特别长的对象所引用
第二是循环引用中的对象定义了__del__函数
此时需要使用模块:objgraph
import objgraph
a = [1,2,3]
b = [4,5,6]
a.append(b)
b.append(a)
objgraph.show_refs(a)
需要将所得的dot转为pnd文件,网址:
https://onlineconvertfree.com/
所得图片:
通过查看引用关系,可以得知a后引用了b列表
如果改成代码:
import objgraph
a = [1,2,3]
b = [4,5,6]
a.append(b)
objgraph.show_refs(b)
此时的图片为:
此时为3items,那么为什么一个是4 items 一个是3items呢?
我可以打开:python Console 进行操作
PyDev console: starting.
Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)] on win32
a = [1,2,3]
b = [4,5,6]
a.append(b)
b.append(a)
a
[1, 2, 3, [4, 5, 6, […]]]
b
[4, 5, 6, [1, 2, 3, […]]]
这样子:
在循环引用中,前3个元素是4 5 6 第4个元素是a
a 呢也是循环的,其中的内容也是1 2 3
就这样循环引用下去
如图:
这样的循环:‘从前有座山’警告,是无穷循环