flask使用debug模式时,存在错误时,会占用设备内存直至服务重启才释放;debug模式会开启一个守护进程(daemon process)

函数调用顺序flask的app.py的run-->werkzeug的serving.py的run_simple-->调用werkzeug的debug的__init__.py里的类DebuggedApplication,这里类有两个dict对象:self.frames = {}
self.tracebacks = {}
。用来存放异常栈信息。

因为flask启动时调用run,所以就会初始化这个类DebuggedApplication,创建两个dict  frames和tracebacks,记录异常栈信息,直到下次应用重启,重新初始化这个类,和这两个对象。

因此在生产环境如果开启debug模式,并且存在错误逻辑导致异常,在不断被触发的情况下,设备早晚会内存不足,而导致应用被kill掉。

可以使用sys.getsizeof(self.frames)查看对象大小。直接修改werkzeug文件,调试--意思是说,修改三方包werkzeug的源代码,打印出异常栈的信息(print sys.getsizeof(self.frames)),这样本地调试(使用pycharm等工具)flask的时候,就可以在控制台看到这个异常栈的大小信息。错误持续发生,这个dict对象就会越来越大

再说下debug模式的守护进程,默认情况下,即app.run()不设置多进程启动的话,不开启debug,只有一个进程,过来的请求,一个一个处理,没有并发能力,顺序执行。在debug模式下,会先启动一个守护进程,然后启动主进程。主进程关掉以后,守护进程自然就关掉了;但是守护进程关掉了,不影响主进程的运行.

守护进程的作用:

简单来说就是,本来并没有 daemon thread,为了简化程序员的工作,让他们不用去记录和管理那些后台线程,创造了一个 daemon thread 的概念。这个概念唯一的作用就是,当你把一个线程设置为 daemon,它会随主线程的退出而退出。关键作用有三个:

  1. background task
  2. only useful when the main program is running
  3. ok to kill

被设置为 daemon 的线程应当满足这三条。第一点需要说一下,比如一个线程需要用 join 执行,那么 daemon 就没有意义了,因为程序总是需要等待它完成才能继续执行。

Some threads do background tasks, like sending keepalive packets, or performing periodic garbage collection, or whatever. These are only useful when the main program is running, and it's okay to kill them off once the other, non-daemon, threads have exited.

Without daemon threads, you'd have to keep track of them, and tell them to exit, before your program can completely quit. By setting them as daemon threads, you can let them run and forget about them, and when your program quits, any daemon threads are killed automatically.

Daemon线程会被粗鲁的直接结束,它所使用的资源(已打开文件、数据库事务等)无法被合理的释放。因此如果需要线程被优雅的结束,请设置为非Daemon线程,并使用合理的信号方法,如事件Event。会不会产生内存泄露?

参考:

1、https://laike9m.com/blog/daemon-is-not-daemon-but-what-is-it,97/

2、https://stackoverflow.com/questions/190010/daemon-threads-explanation

3、https://www.cnblogs.com/xfiver/p/5189732.html

原文地址:https://www.cnblogs.com/shengulong/p/8111184.html