函數調用順序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,它會隨主線程的退出而退出。關鍵作用有三個:
- background task
- only useful when the main program is running
- 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
