使用Python在Threaded COM对象中泄漏内存

时间:2013-05-03 20:49:15

标签: python multithreading com memory-leaks win32com

我正在线程中创建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应该为我照顾这个。任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:5)

事实证明,内存的增加实际上是由于用.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()

答案 1 :(得分:0)

此处链接可能会帮助您release COM in python win32

答案 2 :(得分:0)

对我而言(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()