Ser*_*lov 5 python ssl garbage-collection memory-leaks
Python 2.7.12,Ubuntu 16.04。
简单的服务器应用程序,每 10 秒检查一次电子邮件,并在调试日志中写入没有消息。邮箱真的是空的,所以应用程序真的什么都不做。单线程。不使用任何自定义 C 扩展。通常使用 17 Mb 的内存或类似的东西。
但是几个小时后,可能是一天后,它突然开始在内存中增长到 8 GB,消耗 100% 的 CPU 并且不再写入调试日志。
我阅读了http://tech.labs.oliverwyman.com/blog/2008/11/14/tracing-python-memory-leaks/,在 pdb 下运行我的应用程序,等待一个晚上,看看 objgraph 可以显示。没有。没有广泛增长的对象类型,所有计数都正常(我首先检查正常状态下的应用程序)。
然后我尝试使用https://pythonhosted.org/Pympler/muppy.html像这样:
(Pdb) from pympler import muppy
(Pdb) all_objects = muppy.get_objects()
*** MemoryError:
Run Code Online (Sandbox Code Playgroud)
过了一会儿,我在系统监视器中看到,该进程 pdb 现在仅消耗 2Gb 内存!但是它是单线程的(我用 ps 检查)并且我的应用程序被停止了,所以不知何故 6Gb 就消失了......
故事结束:经过一些实验,我意外地发现如何在这个 pdb 会话中快速达到 100% CPU 和 8-12 Gb。只需调用 gc.collect() 即可。muppy.get_objects() 具有相同的效果。
但是在 pdb 中 gc.collect() 完成后的几分钟内资源会收缩回 2Gb,这似乎在应用中情况不太一样。
顺便说一下,gc.collect() 返回 0。
我怀疑有人破坏了 python 堆,我怀疑 SSL 模块(只是因为它很复杂,没有任何更具体的原因)。我在使用标准 imaplib 通过 SSL 通过 IMAP 连接到邮箱时使用它 + 一些我的代码,使其理解超时(并且我的所有代码都在 Python 中)。
我还能做些什么来查找/修复问题?可能是一些好的内存检查工具或 python 库中的一个已知错误?
伙计们,我明白了!似乎 GC 问题只是视觉效果,真正的问题在这里:
# IMAP with timeouts
class NonBlockingSSL_IMAP(imaplib.IMAP4):
def __init__(self, timeout, *args, **kwargs):
self.timeout = timeout
imaplib.IMAP4.__init__(self, *args, **kwargs)
....
def read(self, size):
readed = 0
buffer = []
# ssl socket is not entirely normal socket, so may be there are some data
# and select do not know about them. So we should dry socket first.
dried = False
while readed < size:
if dried:
self.wait_for(recv=self.sock)
try:
data = self.sslobj.recv(size - readed)
buffer.append(data) # <----- here !!!!
readed += len(data)
except ssl.SSLWantReadError:
dried = True
except socket.error as se:
if se.errno != errno.EAGAIN:
# something bad happened
raise se
dried = True
return ''.join(buffer)
....
def wait_for(self, recv=None, send=None):
rd = [recv] if recv is not None else []
wr = [send] if send is not None else []
ready_r, ready_w, _ = select.select(rd, wr, [], self.timeout)
if not ready_r and not ready_w:
raise socket.error(errno.EAGAIN, "timeout")
Run Code Online (Sandbox Code Playgroud)
如果 recv 返回空字符串,我将其添加到缓冲区,并且不增加读取(因为 len == 0)。它可能是一个无限循环。结果,我们有一个非常大的空字符串列表——根本没有其他对象(因为所有 '' 都是一个对象;你好,objgraph)。只有一个非常非常大的清单。可能Python无法正确处理它,我可以理解。
| 归档时间: |
|
| 查看次数: |
323 次 |
| 最近记录: |