Python smtplib模块内存泄漏

2 投票
3 回答
711 浏览
提问于 2025-04-16 14:17

我写了一个程序,类似于守护进程,用来监控一个文件夹里的文件,只要文件有变化就会发邮件通知我。我用bb-freeze把它编译成了Windows的.exe文件。结果我发现让它运行了几天后,它占用的内存越来越多。

我使用了Heapy来监控这个.py文件的内存使用情况(不是编译后的.exe),发现每次调用一个函数时,内存中对象的数量增加了3个,同时内存使用量也增加了484字节。这个程序用到了smtplib模块,但我搞不清楚内存泄漏是怎么发生的。

from guppy import hpy
import time
import gc

import os
import smtplib
import mimetypes
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.MIMEAudio import MIMEAudio
from email.MIMEImage import MIMEImage
from email.Encoders import encode_base64

def sendMail(subject, text, to='blah@gmail.com', username='more.blah@gmail.com', password='blah', smtpServer='smtp.gmail.com', smtpPort=587):
   gmailUser = username
   gmailPass = password
   recipient = to

   msg = MIMEMultipart()
   msg['From'] = gmailUser
   msg['To'] = recipient
   msg['Subject'] = subject
   msg.attach(MIMEText(text))

   mailServer = smtplib.SMTP(smtpServer, smtpPort)
   mailServer.ehlo()
   mailServer.starttls()
   mailServer.ehlo()
   mailServer.login(gmailUser, gmailPass)
   mailServer.sendmail(gmailUser, recipient, msg.as_string())
   mailServer.quit()

   print('Sent email to "%s"' % recipient)

if __name__=='__main__':
   while True:
      sendMail("Function", "Blah!")
      gc.collect()
      print hpy().heap()
      time.sleep(10)

我在网上看到这段代码就复制过来了。它能工作,但会导致内存泄漏。有人能帮我找出内存泄漏发生的地方吗?? :(

补充:看起来是使用msg.as_string()导致了内存泄漏。如果用简单的文本,比如msg="Blah"替代msg.as_string(),问题就解决了。但这样我就不能添加主题行了。

3 个回答

0

1) 你可以尝试用简单的字符串文本来替代消息的代码。
2) 你可以去掉对ehlo的调用,看看这样是否能解决内存泄漏的问题。(这些调用其实是多余的)

import smtplib


def main() :
    fromaddr = 'ph111@gmail.com'
    toaddrs  = 'ph222@gmail.com'
    msg = 'my simple message'
    username = 'ph111'
    password = 'mypassword'
    server = smtplib.SMTP('smtp.gmail.com:587')
    server.starttls()
    server.login(username,password)
    server.sendmail(fromaddr, toaddrs, msg)
    server.quit()

if __name__ == '__main__':
    main()
0

你有没有使用过Valgrind这个工具?它非常好用,可以帮助你找到程序中的内存泄漏问题。你也可以试试Python 调试器

编辑:

抱歉,我刚注意到你说你在用Windows,而Valgrind在Windows上是不能用的 :(。

不过,你可以看看Python在Windows上用的哪个库来处理加密。根据我在Mac上运行你提供的脚本的结果,发现mailServer.starttls()这一行有内存泄漏,问题可能出在libssl/libcrypto的Python封装上。抱歉如果这对你没有帮助(因为我不是在Windows上)。

1

我敢打赌,你的代码里可能有个循环引用。

补充一下:我稍微改了一下你的代码,变成了:

import time
import gc
import smtplib
import mimetypes
from email.MIMEText import MIMEText

def sendMail(subject, text):
   gmailUser = 'myemail@gmail.com'
   gmailPass = 'mypassword'
   recipient = gmailUser

   msg = MIMEMultipart()
   msg['From'] = gmailUser
   msg['To'] = recipient
   msg['Subject'] = subject
   msg.attach(MIMEText(text))

   mailServer = smtplib.SMTP('smtp.gmail.com', 587)
   mailServer.starttls()
   mailServer.login(gmailUser, gmailPass)
   mailServer.sendmail(gmailUser, recipient, msg.as_string())
   mailServer.quit()

   print('Sent email to "%s"' % recipient)

if __name__=='__main__':
   gc.set_debug(gc.DEBUG_LEAK)
   for item in range(1000):
      sendMail("Function", "Blah!")
      gc.collect()
      time.sleep(2)

我用的Python和C++编译器不支持Guppy,所以没法测试输出(也许你那边也不行?)。不过我可以告诉你,我观察了垃圾回收的输出和一些内存统计数据,发现代码里没有明显的变化或内存泄漏问题。主要的改动是:去掉了SMTP.ehlo()的调用(这个是多余的),去掉了默认的函数参数(我怀疑这些参数可能会作为对象留在内存中,只要函数在作用域内,就可能会让SMTP对象一直存在)。所以你可以试试先做一个改动,再做另一个,看看哪个能解决你的问题。

可以看看这篇文章,里面有一些帮助和工具。

撰写回答