由每个API调用引起的Flask应用程序内存泄漏
我的FlaskAPI有一个很小的内存泄漏,多次API调用都会导致我的应用程序达到内存限制并崩溃。我一直在试图弄清楚为什么一些内存没有被释放,但到目前为止还没有成功,我相信我确实知道其来源。谢谢你的帮助 不幸的是,我无法共享代码,但为了用英语描述它,我的flask应用程序提供了一个API端点,供用户执行以下操作(一次调用完成):由每个API调用引起的Flask应用程序内存泄漏,flask,memory-leaks,Flask,Memory Leaks,我的FlaskAPI有一个很小的内存泄漏,多次API调用都会导致我的应用程序达到内存限制并崩溃。我一直在试图弄清楚为什么一些内存没有被释放,但到目前为止还没有成功,我相信我确实知道其来源。谢谢你的帮助 不幸的是,我无法共享代码,但为了用英语描述它,我的flask应用程序提供了一个API端点,供用户执行以下操作(一次调用完成): 根据提供的ID从MongoDB中提取一些数据 根据返回的内容,使用库构建文档对象并将其保存到磁盘 最后,我将保存到磁盘的内容上传到S3存储桶,然后删除磁盘上的内容 据我所
导入gc
导入操作系统
导入tracemalloc
导入psutil
从烧瓶进口烧瓶
app=烧瓶(名称)
全局变量=[]
process=psutil.process(os.getpid())
tracemalloc.start()
s=无
def_get_foo():
全局变量
全局变量append([1,“a”,3,True]*10000)#这是我们(放大的)内存泄漏
返回{'foo':True}
@app.route(“/foo”)
def get_foo():
gc.collect()#没有帮助
return\u get\u foo()
@app.route(“/memory”)
def print_memory():
返回{'memory':process.memory_info().rss}
@app.route(“/snapshot”)
def snap():
全球s
如果不是,则:
s=tracemalloc.take_snapshot()
返回“拍摄的快照\n”
其他:
行=[]
top_stats=tracemalloc.take_snapshot()。将_与(s,'lineno')进行比较
对于排名靠前的统计数据[:5]:
行。追加(str(stat))
返回“\n”。连接(行)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
app.run()
内存泄漏在第17行,并由注释指示。不幸的是,这种情况很少发生
如您所见,我已尝试通过手动调用垃圾回收来修复内存泄漏,即在端点“foo”处返回值之前调用gc.collect()。但这并不能解决问题
查找内存泄漏
为了查明是否存在内存泄漏,我们多次调用端点“foo”,并测量API调用前后的内存使用情况。另外,我们将拍摄两张tracemalloc
快照。是跟踪Python分配的内存块的调试工具。如果使用Python3.4+,则它位于标准库中
以下脚本应阐明该策略:
导入请求
#预热,这样就不会测量烧瓶内部内存使用情况
对于范围(10)内的uu:
请求。获取('http://127.0.0.1:5000/foo')
#API调用前的内存使用
resp=requests.get('http://127.0.0.1:5000/memory')
打印(f'Memory-before-API调用{int(resp.json().get(“Memory”))})
#获取第一个内存使用快照
resp=requests.get('http://127.0.0.1:5000/snapshot')
#启动一些API调用
对于范围(50)内的:
请求。获取('http://127.0.0.1:5000/foo')
#之后的内存使用情况
resp=requests.get('http://127.0.0.1:5000/memory')
打印(f'Memory after-API调用:{int(resp.json().get(“Memory”))})
#拍摄第二张快照并打印结果
resp=requests.get('http://127.0.0.1:5000/snapshot')
pprint(分别为文本)
输出:
Memory before API call 35328000
Memory after API call: 52076544
('.../stackoverflow/flask_memory_leak.py:17: '
'size=18.3 MiB (+15.3 MiB), count=124 (+100), average=151 KiB\n'
'...\\lib\\tracemalloc.py:387: '
'size=536 B (+536 B), count=3 (+3), average=179 B\n'
'...\\lib\\site-packages\\werkzeug\\wrappers\\base_response.py:190: '
'size=512 B (+512 B), count=1 (+1), average=512 B\n'
'...\\lib\\tracemalloc.py:524: '
'size=504 B (+504 B), count=2 (+2), average=252 B\n'
'...\\lib\\site-packages\\werkzeug\\datastructures.py:1140: '
'size=480 B (+480 B), count=1 (+1), average=480 B')
API调用前的内存35328000
API调用后的内存:52076544
(“…/stackoverflow/flask_memory_leak.py:17:”
'大小=18.3兆字节(+15.3兆字节),计数=124(+100),平均值=151千字节\n'
'…\\lib\\tracemalloc.py:3
Memory before API call 35328000
Memory after API call: 52076544
('.../stackoverflow/flask_memory_leak.py:17: '
'size=18.3 MiB (+15.3 MiB), count=124 (+100), average=151 KiB\n'
'...\\lib\\tracemalloc.py:387: '
'size=536 B (+536 B), count=3 (+3), average=179 B\n'
'...\\lib\\site-packages\\werkzeug\\wrappers\\base_response.py:190: '
'size=512 B (+512 B), count=1 (+1), average=512 B\n'
'...\\lib\\tracemalloc.py:524: '
'size=504 B (+504 B), count=2 (+2), average=252 B\n'
'...\\lib\\site-packages\\werkzeug\\datastructures.py:1140: '
'size=480 B (+480 B), count=1 (+1), average=480 B')
import sys
import os
import site
from waitress import serve
dir_path = os.path.dirname(__file__)
sys.path.append(os.path.abspath(dir_path))
venv_packages = os.path.abspath(os.path.join(dir_path, 'venv', 'lib', 'site-packages'))
sys.path.append(venv_packages)
site.addsitedir(venv_packages)
from dotenv import load_dotenv
dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
load_dotenv(dotenv_path)
from settings import API_HOST, API_PORT
from app import app as application
serve(application, host=API_HOST, port=API_PORT)
. venv/bin/activate
pip install waitress
python app.waitress
py -3 -m pip install waitress
py app.waitress
Python 3.7.9
waitress 1.4.1
Flask 1.1.2
Flask-Cors 3.0.10
Flask-JWT-Extended 3.25.0
python-dotenv 0.10.3