关于一个内存泄露的惨痛教训
关于python 的引用传递的惨痛
def data_iterator(datas):
"""
遍历数据,删除不合格项,返回正常数据
:param datas:
:return:
"""
valid_data = {}
invalid_data = []
for data in datas:
# 进行详细地址判断
if "_id" in data:
data.pop("_id")
format_data_eval(data) #重点
format_data_add_key(data) #重点
i = format_data_position(data)
if i == 1:
invalid_data.append(data)
else:
if data["extend"]['date'] not in valid_data:
valid_data[data["extend"]['date']] = []
valid_data[data["extend"]['date']].append(data)
return valid_data, invalid_data
上述部分代码是在进行数据修复。
惨痛教训来了
因为知道python 没有值传递,都是引用传递,因为:
Python 中一切皆为对象,数字是对象,列表是对象,函数也是对象,任何东西都是对象。而变量是对象的一个引用(又称为名字或者标签),对象的操作都是通过引用来完成的。
所以我在代码中,直接通过引用遍历中的对象,直接进行操作对象,完成数据格式化
但是当代码运行数据到1000000 的时候,打开任务管理发现,内存在逐渐增加。发现内存泄露
这里首先说下,内存泄露和内存溢出的含义和区别:
- 内存泄露:就是你引用的对象,结果你用完了之后,没有清理,但是python 的垃圾回收机制判断他还是在用,只能到代码结束,才能够释放。
- 内存溢出:就是相当于你想将100g的文件一次性读入的内存中,但是内存没有这么大,导致代码崩掉。
- 详细看这个博客
解决问题
- 整个代码中,出现了方法的调用的情况,
- 在前面说到,python是引用传递,所以我直接采用方法嵌入遍历中,通过地址的一致则格式化数据,
- 但是方法在调用过程中,是进行一个相当于深度复制的操作,例如下面demo
4. 可以发现,指针在方法中,发生了变化,,而当这个文件结束后,还是调用了同样的方法那么a还是在使用,并不能自动销毁,但是发现部分还是进行了销毁,那我原先的代码,所以会跑到1千万才会崩。
- 但是指定返回值,会标记这个方法的结束,会让python的垃圾回收器自动回收这个地址。
- 所在通过任务管理器查看,发现内存会在这时候增大,但是当运行完成后,会变小的一个 过程。
正确的写法
def data_iterator(datas):
"""
遍历数据,删除不合格项,返回正常数据
:param datas:
:return:
"""
valid_data = {}
invalid_data = []
for data in datas:
# 进行详细地址判断
if "_id" in data:
data.pop("_id")
data = format_data_eval(data)
i = format_data_position(data)
if i == 1:
invalid_data.append(data)
else:
if data["extend"]['date'] not in valid_data:
valid_data[data["extend"]['date']] = []
valid_data[data["extend"]['date']].append(data)
return valid_data, invalid_data