内存溢出问题排查
前言
系统中需要运行一个脚本,脚本中通过项目配置,会调用Django的model,而且脚本中是一个轮询机制,会不断的查库操作,公司内部监控平台爆出服务器内存占用过高,排查到该脚本。
问题
内存无法释放,持续增长
- 分析内存工具,tracemalloc 工具包;
分析过程
1.代码
import tracemalloc
class Ticker(object):
def __init__(self):
pass
def run(self):
""""逻辑块"""
a = tracemalloc.take_snapshot()
e = a.compare_to(c, "lineno")
for elem in e[0:10]:
elem = r"{}".format(elem)
sub = re.search(r".*\\(.*)\\(.*):.*: (size=.*), count.*", elem)
if sub is None:
print(elem)
elif "tracemalloc.py" == sub.group(2):
pass
else:
print(sub.group(1), sub.group(2), "----", sub.group(3))
print("++++++++++++++++++++++++++++++++++++++++")
if __name__ == '__main__':
T = Ticker()
tracemalloc.start()
c = tracemalloc.take_snapshot()
while True:
asd = r.get("ht_mq_power")
if asd == "0":
print("redis ht mq_power off")
sys.exit()
T.run()
time.sleep(0.3)
2.》》》运行结果
<frozen importlib._bootstrap_external>:487: size=101 KiB (+101 KiB), count=1040 (+1040), average=100 B
redis connection.py ---- size=64.1 KiB (+64.1 KiB)
fields related.py ---- size=21.9 KiB (+21.9 KiB)
utils functional.py ---- size=14.7 KiB (+14.7 KiB)
models options.py ---- size=10.7 KiB (+10.7 KiB)
lib _weakrefset.py ---- size=10104 B (+10104 B)
models expressions.py ---- size=8736 B (+8736 B)
pymqi __init__.py ---- size=8584 B (+8584 B)
lib _weakrefset.py ---- size=8512 B (+8512 B)
++++++++++++++++++++++++++++++++++++++++
<frozen importlib._bootstrap_external>:487: size=101 KiB (+101 KiB), count=1040 (+1040), average=100 B
redis connection.py ---- size=64.1 KiB (+64.1 KiB)
fields related.py ---- size=23.2 KiB (+23.2 KiB)
utils functional.py ---- size=14.7 KiB (+14.7 KiB)
models options.py ---- size=10.7 KiB (+10.7 KiB)
lib _weakrefset.py ---- size=10104 B (+10104 B)
models expressions.py ---- size=8808 B (+8808 B)
pymqi __init__.py ---- size=8584 B (+8584 B)
lib _weakrefset.py ---- size=8512 B (+8512 B)
3.分析
通过运行上面段的代码,会发现不断打印出每一次轮询内存占用详情,
(sub.group(1), sub.group(2), “----”, sub.group(3),
这里的:sub.group(1), sub.group(2) 代表的是哪个文件内存占用问题,
sub.group(3) 代表的是占用大小
分析出来: 1.models expressions.py这个文件占用会随着轮询次数的增长越来越大,联想到是否是Django的惰性加载机制,也就是会将查询SQL进行缓存,
2.fields related.py这个文件内存占用一样越来越大,这个是一个Django外键的文件,是否在逻辑块内有对象循环引用导致,Python的垃圾回收机制无法回收,所以引用gc模块,手动删除创建的每个Django查询对象,并主动进行垃圾回收
4.验证
通过在逻辑块中加入查询执行SQL代码:from django.db import connection import gc """"逻辑代码""" print(connection.queries)
5.结论
事实上打印的SQL一直在递增,都是之前的执行SQL累加在一个list里面
解决方案
from django import db
def func():
"""logic code"""
obj = Model.object.get(id=123)
del obj
gc.collect()
db.reset_queries() # 清除缓存的执行SQL