【问题标题】:Memory Leak in Threaded COM Object with Python使用 Python 的线程 COM 对象中的内存泄漏
【发布时间】:2013-04-28 08:29:41
【问题描述】:

我正在一个线程中创建一个 COM 客户端并使用该客户端执行多个操作。每个线程都是从使用 Python 的 socketserver 模块的服务器产生的,该模块具有内置的线程支持。

当我加载和使用这个 COM 对象时,python.exe 的内存使用量会出现预期峰值。对于 10 个并发线程,内存使用量峰值约为 500Mb。但是,当操作完成并且 COM 对象明显释放时,该进程使用的内存比以前增加了 50Mb。如果我随后使用同一台服务器生成 10 个额外的线程,那么在这些 COM 对象关闭后,python.exe 将额外使用 13Mb。最终,每 10 个额外的并发线程在完成后会增加大约 6Mb。当我结束整个python.exe进程时,所有的内存都被释放了。

我已经简化了下面的代码来模拟socketserver如何使用threadding,问题是完全一样的。

import win32com.client
import threading
import pythoncom

def CreateTom():
    pythoncom.CoInitialize()
    tom = win32com.client.Dispatch("TOM.Document")
    tom.Dataset.Load("FileName")
    tom.Clear()
    pythoncom.CoUninitialize()

for i in range(50):
    t = threading.Thread(target = CreateTom)
    t.daemon = False
    t.start()

我知道我不太可能在特定的 COM 库(它是市场研究中使用的 IBM 产品,称为 TablesObjectModel)获得任何支持。但是,我想知道是否有任何事情,任何事情,我可以做些什么来释放这个记忆。我已经阅读了 COM 中的公寓,但听起来 pythoncom.CoInitialize 应该为我处理这个问题。任何帮助将不胜感激。

【问题讨论】:

  • 这可能并不完全相关,但请尝试使用线程池而不是生成新线程,而不像您发布的示例那样定义任何约束。
  • TOM.Document 是在进程内还是进程外?您确定“一段时间”后内存没有释放吗?例如,使用垃圾收集器技术编写的 COM 对象可能会发生这种情况(例如 .NET,尽管我怀疑 TOM.Document 是用 .NET 编写的)
  • TOM.Document 正在处理中。它不会在 python.exe 之外派生另一个进程
  • 你考虑过用子进程代替线程吗?
  • 为 COM 对象创建单独的子进程没有成功。由于跨进程通信的数量,我无法让它与 Windows 上的管道一起使用。

标签: python multithreading com memory-leaks win32com


【解决方案1】:

事实证明,内存的增加实际上是由于用 .NET 编写的 COM 对象,与线程无关。 Here 是任务管理器的详细描述,提供了有关 .NET 应用程序内存使用的误导性信息。为了解决这个问题,我在我的代码中添加了以下内容,我已经准备好了。希望其他人在开始努力寻找代码中的内存泄漏之前阅读此响应。

from win32process import SetProcessWorkingSetSize
from win32api import GetCurrentProcessId, OpenProcess
from win32con import PROCESS_ALL_ACCESS

import win32com.client
import threading
import pythoncom

def CreateTom():
    pythoncom.CoInitialize()
    tom = win32com.client.Dispatch("TOM.Document")
    tom.Dataset.Load("FileName")
    tom.Clear()
    pythoncom.CoUninitialize()
    SetProcessWorkingSetSize(handle,-1,-1) #Releases memory after every use

pid = GetCurrentProcessId()
handle = OpenProcess(PROCESS_ALL_ACCESS, True, pid)

for i in range(50):
    t = threading.Thread(target = CreateTom)
    t.daemon = False
    t.start()

【讨论】:

    【解决方案2】:

    这里的链接可以帮助你release COM in python win32

    【讨论】:

    • 谢谢你,这对确认我的脚本中没有剩余的 COM 引用非常有帮助,因为 pythoncom._GetInterfaceCount() = 0
    【解决方案3】:

    对我有帮助 (based)::

    from comtypes.automation import IDispatch
    from ctypes import c_void_p, cast, POINTER, byref
    
    def release_reference(self, obj):
        logger.debug("release com object")
        oleobj = obj._oleobj_
        addr = int(repr(oleobj).split()[-1][2:-1], 16)
    
        pointer = POINTER(IDispatch)()
        cast(byref(pointer), POINTER(c_void_p))[0] = addr
        pointer.Release()
    

    【讨论】:

      猜你喜欢
      • 2011-06-17
      • 2012-12-13
      • 2010-10-13
      • 2018-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-29
      • 2013-12-18
      相关资源
      最近更新 更多