2013-05-03 1 views
3

Я создаю COM-клиент в потоке и выполняю несколько операций с этим клиентом. Каждый поток генерируется с сервера, который использует модуль Python socketserver, который имеет встроенную поддержку потоковой передачи.Утечка памяти в потоковом COM-объекте с Python

Когда я загружаю и используя этот объект COM, ожидается ожидаемый всплеск использования памяти python.exe. При использовании 10 параллельных потоков максимальный объем памяти составляет около 500 МБ. Однако, когда операции завершены, и COM-объект, по-видимому, выпущен, есть 50Mb дополнительной памяти, используемой процессом, чем раньше. Если я затем создаю 10 дополнительных потоков, использующих один и тот же сервер, добавляется еще 13 Мб, используемое python.exe после закрытия COM-объектов. В конечном итоге каждые 10 дополнительных одновременных потоков добавляют примерно 6 МБ после их завершения. Когда я заканчиваю весь процесс 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 должен позаботиться об этом для меня. Любая помощь будет оценена по достоинству.

+0

Это может быть не совсем уместно, но попробуйте использовать пул потоков вместо нерест новый поток без определения каких-либо ограничений, как, например, вы вывешенный. – woozyking

+0

Является ли TOM.Document в процессе или вне процесса? Вы уверены, что память не выпущена после «некоторого времени»? Это может произойти, например, с COM-объектами, написанными в технологиях с сборщиком мусора (например, .NET, хотя я сомневаюсь, что TOM.Document написан на .NET) –

+0

TOM.Document находится в процессе. Это не вызывает другой процесс за пределами python.exe. –

ответ

5

Как выясняется, это увеличение памяти было на самом деле связано с COM-объекта, написанной в .NET и не имеет ничего общего с многопоточности. 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() 
0

здесь ссылка может помочь вам release COM in python win32

+0

Спасибо за это, было очень полезно подтвердить, что в моем скрипте нет ссылок на COM в качестве скрипта pythoncom._GetInterfaceCount() = 0 –

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()