2

我编写了一个守护进程来监视目录中的文件,并在任何文件发生更改时发送电子邮件。我使用 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() 可以解决此问题。但这不允许我添加主题行。

4

3 回答 3

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)

Guppy 不适用于我拥有的 python 和 c++ 编译器版本,因此我无法测试该输出(也许它也不适用于您?)。我可以告诉你的是,我在进程资源管理器中查看了垃圾收集输出和一些内存统计信息,并没有发现该代码中存在重大差异或泄漏问题。关键更改:删除了对 SMTP.ehlo() 的调用(不必要),删除了默认函数参数(我怀疑这些可能会作为对象保留下来,只要函数在范围内就可以被引用,这可能会以某种方式保留 SMTP周围的物体)。因此,您可能想先尝试一个,然后再尝试另一个,看看哪个可以解决您的问题。

查看这篇文章以获得帮助和一些工具。

于 2011-03-23T18:14:40.433 回答
0

你可以访问Valgrind吗?这是查找内存泄漏的好工具。您也可以尝试使用Python Debugger

编辑:

抱歉,刚刚注意到您说您使用的是 Windows,而 valgrind 不适用于 :(。

无论如何,您可能想查看 Python for Windows 用于加密的任何库。在我的 Mac 上,您提供的脚本显示了mailServer.starttls()调用周围的内存泄漏,罪魁祸首似乎是 libssl/libcrypto 周围的 python 包装器。抱歉,如果这根本没有帮助(非 Windows ......)。

于 2011-03-23T17:52:32.637 回答
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()
于 2011-03-23T18:20:20.147 回答