1

我正在编写一个扭曲的 PB 应用程序,它似乎使用了大量的内存,当用户断开连接时似乎永远不会释放。

我有一个 pb.Root 对象,客户端连接到该对象并调用一个返回 pb.Referenceable 对象的远程方法,该对象在创建时将大量信息(大约 2GB 的数据)读入内存以加快操作速度。该对象以及有关客户端的一些其他信息被插入到列表中。

当客户端与服务器断开连接时,我对该对象调用一些清理操作以删除对正在存储的缓存对象的引用。chunkCache 是我存储数据的字典。

def disconnected(self):
    self.connected = False
    self.chunkCache = None
    self.cur.close()

一旦客户端根据 top never drop 断开内存使用情况,它仍然会显示 2Gb。

我应该担心这一点,还是会在需要时释放分配的内存,或者如果没有任何想法,我该如何释放这些内存?它是在创建对象时创建的,而不是在其他任何地方传递。

在该对象内部,我确实有一个 deferToThread 调用,这是否会阻止正在释放的项目?

我在 Linux 上使用 python 2.7。

更新:

我很困惑,我刚刚__del__向我的对象添加了自定义方法并在其中放置了一个打印语句,它们正在被删除,那么为什么内存使用量永远不会下降?

谢谢

院长

4

1 回答 1

2

在大多数当前的操作系统上,动态内存的分配发生在通常称为堆的地方(不要与同名的数据结构混淆),它本质上只是一个从某个基地址开始并扩展到基地址的连续区域地址加上堆的当前大小。进程从一个小堆开始——通常只有几页——它会根据需要通过以系统页面大小的正整数倍数为单位向上扩展段来增长。在该空间内,分配/解除分配会创建更小的内存块以满足程序的需求,以及分配器需要跟踪哪些内存已分配和未分配的任何元数据。这些较小的内存块可能会随着时间的推移而被释放,使堆段基本上是空的,但除非它是专门编码的,堆段很少缩小。因此,长时间运行的进程往往会出现一个与其峰值使用量一样大的堆段。如果需要为其他进程释放物理内存,则将倾向于将正在使用但不再使用的页面移出以进行交换,但进程映像仍然看起来“大”,并且通常不会缩小没有被重新启动。有一些方法可以使用其他内存分配机制(例如,为内存空间映射一个临时文件,并在完成后取消映射并删除它),但程序需要专门编码才能这样做。使用标准库分配例程的 C 或 C++ 编写的应用程序(一个长时间运行的进程似乎有一个与其峰值使用量一样大的堆段。如果需要为其他进程释放物理内存,则将倾向于将正在使用但不再使用的页面移出以进行交换,但进程映像仍然看起来“大”,并且通常不会缩小没有被重新启动。有一些方法可以使用其他内存分配机制(例如,为内存空间映射一个临时文件,并在完成后取消映射并删除它),但程序需要专门编码才能这样做。使用标准库分配例程的 C 或 C++ 编写的应用程序(一个长时间运行的进程似乎有一个与其峰值使用量一样大的堆段。如果需要为其他进程释放物理内存,则将倾向于将正在使用但不再使用的页面移出以进行交换,但进程映像仍然看起来“大”,并且通常不会缩小没有被重新启动。有一些方法可以使用其他内存分配机制(例如,为内存空间映射一个临时文件,并在完成后取消映射并删除它),但程序需要专门编码才能这样做。使用标准库分配例程的 C 或 C++ 编写的应用程序(如果需要为其他进程释放物理内存,则将倾向于将其移出以进行交换,但进程映像仍然看起来“很大”,并且通常不会在不重新启动的情况下缩小。有一些方法可以使用其他内存分配机制(例如,为内存空间映射一个临时文件,并在完成后取消映射并删除它),但程序需要专门编码才能这样做。使用标准库分配例程的 C 或 C++ 编写的应用程序(如果需要为其他进程释放物理内存,则将倾向于将其移出以进行交换,但进程映像仍然看起来“很大”,并且通常不会在不重新启动的情况下缩小。有一些方法可以使用其他内存分配机制(例如,为内存空间映射一个临时文件,并在完成后取消映射并删除它),但程序需要专门编码才能这样做。使用标准库分配例程的 C 或 C++ 编写的应用程序(malloc/freenew/ delete),包括 Python 编译器/解释器,将倾向于表现出上述行为。

于 2014-01-08T16:50:36.037 回答