5

我有一个应用程序,我使用请求从服务器下载 .mp3 文件。

代码如下所示:

self.client = requests.session(headers={'User-Agent': self.useragent})

def download(self, url, name):
    request = self.client.get(url)

    with open(name, "wb") as code:
        code.write(request.content)

    print "done"

问题是当下载完成时,python 不会清除内存,所以每次我下载 mp3 时,应用程序的内存使用量会随着 mp3 的大小而增加。内存没有再次被清除,导致我的应用程序使用了大量内存。

我认为这与我如何保存文件或 requests.session 的工作方式有关。

有什么建议么。

编辑:这里是代码: https ://github.com/Simon1988/VK-Downloader

相关部分在 lib/vklib.py

4

2 回答 2

5

除了您不了解内存分配的工作原理之外,我认为这里没有实际问题。

当 Python 需要更多内存时,它会要求操作系统提供更多内存。用完该内存后,它通常不会将其返回给操作系统;相反,它会为以后的对象保留它。

因此,当您打开第一个 10MB mp3 时,您的内存使用量会从 3MB 变为 13MB。然后释放该内存,但仍为 13MB。然后你打开第二个 10MB 的 mp3,但它重用了相同的内存,所以你仍然是 13MB。等等。

在您的代码中,您正在为每次下载创建一个线程。如果您一次有 5 个线程,全部使用 10MB,显然这意味着您使用的是 50MB。并且这 50MB 不会被释放。但是,如果您等待它们完成,然后再进行 5 次下载,它将再次重复使用相同的 50MB。

由于您的代码不会以任何方式限制线程数量,因此没有什么(CPU 速度和上下文切换成本不足)可以阻止您启动数百个线程,每个线程使用 10MB,即千兆字节的 RAM。但是只要切换到一个线程池,或者如果有太多的工作等,不让用户开始更多的下载,等等,就可以解决这个问题。

所以,通常,这不是问题。但如果是这样,有两种方法可以解决它:

  1. 创建一个子进程(例如,通过multiprocessing模块)来完成占用内存的工作。在任何现代操作系统上,当一个进程消失时,它的内存就会被回收。这里的问题是,一遍又一遍地分配和释放 10MB 实际上会减慢系统速度,而不是加快系统速度——而且进程启动的成本(尤其是在 Windows 上)会使情况变得更糟。因此,您可能希望将大量作业分拆给 ac 子进程。

  2. 不要一次将整个事情读入内存;使用流式 API 而不是整个文件 API。使用requests,这意味着stream=True在初始请求中设置,然后通常使用r.raw.read(8192)r.iter_content()r.iter_lines()循环而不是访问r.content

于 2013-01-11T01:16:54.977 回答
4

您可以尝试分块流式传输内容:

def download(self, url, name):
    request = self.client.get(url, stream=True)  # `prefetch=False` for older
                                                 # versions of requests
    with open(name, "wb") as code:
        for chunk in request.iter_content(1024):
            if not chunk:
                break

            code.write(chunk)
于 2013-01-11T01:07:38.720 回答