Тема: Переполнение памяти во время работы макроса

Добрый день! Когда макрос обрабатывает большое количество (~1000) выделенных документов или несколько раз запускаем макрос для обработки небольшого количества документов,   может переполниться память. Зависают приложения. Приходится перезагружать компьютер.
В макросе после работы с очередным объектом выполняю
SET имя=Nothing.
Что Вы порекомендуете использовать в скрипте для очистки памяти?

Re: Переполнение памяти во время работы макроса

Здравствуйте. Ох уж эта память. Сложно рекомендовать, так как рекомендации иногда понимаются неправильно, потом получается только хуже :(.

А большой скрипт? Оптимизировать и ускорить можно значительно. Щас темку кину по оптимизации (ну это по скорости). Нужно простой примерчик который вызывает проблемы, мы попробуем показать как его можно оптимизировать.

Re: Переполнение памяти во время работы макроса

Вот ссылочка на тему https://forum.technologics.ru/topic1441.html.
Если понять что у вас происходит с памятью, попробуем сделать примеры по такой оптимизации.

Спасибо сказали: snake1

Re: Переполнение памяти во время работы макроса

И еще по памяти.  Часто бывает что это в TechnologiCS проблема, при обычной работе вы этого не заметите, а при использовании в АПИ многие вещи вылезти могут. Так что тут не всегда оптимизация поможет. И нужно на каждой версии смотреть.

Re: Переполнение памяти во время работы макроса

Отправила скрипт личным сообщением на Ваше имя. Получили?

Re: Переполнение памяти во время работы макроса

Да получил. Посмотрим, точную версию TechnologiCS скажите пожалуйста.

Re: Переполнение памяти во время работы макроса

TCS 5.0.2.0(9328)
Спасибо за примеры оптимизации

Re: Переполнение памяти во время работы макроса

Ну в вашем случае все просто.

Объекты, которые создаются от Application как правило глобальные, и создаются один раз на весь сеанс работы программы. Таким образом мы можем безопасно писать такой код

Do While Not TCSApp.DocParams.DBTree.Selected.ParentNode Is Nothing '
  rNode=TCSApp.DocParams.DBTree.Selected.ParentNode.nodeid 
  TCSApp.DocParams.DbTree.DbNodeByNodeId(rNode).Selected = True  
Loop 

так как в течении всей работы объект TCSApp.DocParams будет один и тот же.

тоже самое относится и к строке  (которая у вас выполняется в цикле)


Set DOC_module=TCSApp.SingleDoc( CSDN_ … sInteger)

Это объект полученный от Application, и он тоже создается один раз (но один раз для каждого документа). При интенсивной работе это может вызвать проблемы с памятью.

Для того чтобы управлять данным поведением, предназначены методы

Таким образом, с помощью данных методов мы можем управлять временем жизни нужны нам объектов (НО!! не надо пользоваться этим повсеместно, могут быть другие побочные эффекты, которые вместо  контроля над объектами дадут серьезные проблемы)

Если ваш код переписать вот к таком виду

 Set DOC_module=TCSApp.SingleDoc( CSDN_Const.dspSearchDocByDocID,Vib_Module.Properties("PRJ_ID").AsInteger)
 If  Not DOC_module Is Nothing Then 
   ................
   DOC_module.UserModuleName = DOC_module.UniqueUserModuleName   
   Call TCSApp.DeleteModuleByUserModuleName(DOC_module.UserModuleName)
 End If

вы будете уничтожать созданные вами объекты.
Или еще лучше вот к такому виду, чтобы еще меньше  сделать вероятность влияния ошибки ( поскольку UniqueUserModuleName создаст уникальное имя, которое никому не будет известно).

 Call TCSApp.DeleteModuleByUserModuleName("MyUniqueName") 
  For ....
    Set DOC_module=TCSApp.SingleDoc( CSDN_Const.dspSearchDocByDocID,Vib_Module.Properties("PRJ_ID").AsInteger)
    If  Not DOC_module Is Nothing Then 
       DOC_module.UserModuleName = "MyUniqueName"
       ................
      Call TCSApp.DeleteModuleByUserModuleName( "MyUniqueName" )
    End If
 Next

надеюсь это поможет.

Спасибо сказали: snake1

Re: Переполнение памяти во время работы макроса

Спасибо!
Добавила к каждому
SET имя= объект
предложенные методы.

НО!! не надо пользоваться этим повсеместно, могут быть другие побочные эффекты, которые вместо  контроля над объектами дадут серьезные проблемы

Хочу уточнить, во избежание побочных эффектов, где этого делать не следует?

И ещё надо ли использовать в данном случае SET имя=Nothing?

Re: Переполнение памяти во время работы макроса

SET имя=Nothing?

По желанию. Скриптовый движок сам отлично отслеживает все ссылки, так что особого смысла в таком коде нет, кроме может красоты. Бывает правда необходимо уничтожить объект в определенном месте программы, или просто ссылку убить дабы исключить последующее использование, тогда конечно требуется подобный код. Или скажем после вот такого кода

DOC_module.UserModuleName = DOC_module.UniqueUserModuleName   
Call TCSApp.DeleteModuleByUserModuleName(DOC_module.UserModuleName)
....
MsgBox DOC_module.UserModuleName

обращение к DOC_module после вызова DeleteModuleByUserModuleName вызовет ошибку, так как DOC_module в скрипте существует и валиден, а внутренний объект TechnologiCS уже уничтожен. В этом случае сброс DOC_module может быть и полезен, хотя система все равно отслеживает это.

По поводу

не надо пользоваться этим повсеместно

скажем так. Как я говорил выше, объекты, которые создаются от Application как правило глобальные, и создаются один раз на весь сеанс работы программы. Остальные объекты, полученные через Properties, ChildModules  и пр. уничтожаются автоматически, и никаких дополнительных действий с ними не требуется. Если же начать пользоваться приведенными выше функциями, то наоборот мы получим кучу глобальных объектов, да еще и с уникальным именем, до которых потом вы уже вряд ли достучитесь, а автоматически они уже не уничтожатся.

Вообще смысл вышеприведенных методов не столько для того, чтобы делать удаление, а для того чтобы можно было создавать свои собственные глобальные настройки и хранить их между запусками скриптов. Способы применения зависят от конкретных задач.
Пример. Запускается скрипт, который просит пользователя сделать кучу настроек, или выбрать что либо. Затем пользователь запускает другой скрипт, которому тоже нужны эти же самые  настройки. Мы можем через глобальные объекты создать (или запомнить уже существующий) некий нужный нам модуль и на старте скрипта попытаться найти его, если не нашли, то запустить выбор/создание настроек.

Поэтому и рекомендации по использованию дать сложно, но главное не считать DeleteModuleByUserModuleName панацеей от проблем с памятью и усложнять код постоянными его вызовами.

Кроме того, в вашем примере скорей всего можно было вообще обойтись без создания объекта TCSApp.SingleDoc( CSDN_ … sInteger), так как если он запускается например из режима Папки, то все необходимые свойства для работы не сильно отличаются от доступных в ISingleDoc (а вот в выборках к сожалению не так). Ну тут надо смотреть универсальность/гибкость, и без  TCS API Explorer тут никак не обойтись.

Re: Переполнение памяти во время работы макроса

Олег, спасибо за объяснение.
Хотя проблема с переполнением памяти не возникает, теперь у меня сомнения на счёт своих исправлений.

Я ведь добавила выше указанные методы ко всем объектам:
1) Set ParamValueList = TCSApp.DocParams.Properties("VALUELIST").AsIDispatch 
2) Set DOC_module=TCSApp.SingleDoc( CSDN_Const.dspSearchDocByVerID,Vib_Module.Properties("PRJVER_ID").AsInteger)
3) Set ATR_module = DOC_module.Properties("PARAMS_VALUES").AsIDispatch   
Причём ко 2 и 3 объектам по 2 раза (цикл проверки и цикл замены).

Правильно ли я поняла, достаточно было применить методы только к DOC_module? остальное избыточно?

Остальные объекты, полученные через Properties, ChildModules  и пр. уничтожаются автоматически, и никаких дополнительных действий с ними не требуется.

Значит, когда в цикле просто повторяется присваивание 

Set ATR_module = DOC_module.Properties("PARAMS_VALUES").AsIDispatch

предыдущий ATR_module автоматически уничтожился (и память освободил)? и можно на этот счёт не беспокоиться?

И второй момент. Макрос запускается из режима "Выборки". Есть другой вариант обратиться к атрибутам документа, минуя TCSApp.SingleDoc?

Re: Переполнение памяти во время работы макроса

Поясню, для каждого объекта создаю конкретное уникальное имя. Т.е. всего 3 уник. имени фигурирует.

Re: Переполнение памяти во время работы макроса

Правильно ли я поняла, достаточно было применить методы только к DOC_module? остальное избыточно?

абсолютно верно.

предыдущий ATR_module автоматически уничтожился (и память освободил)? и можно на этот счёт не беспокоиться?

именно так.

В принципе для того чтобы узнать объект глобальный (создается один раз и сам не уничтожается) или уничтожаемый вместе со ссылкой предназначается свойство IModule.SingleInstance. Правда с того времени как он появился в АПИ много новых возможностей появилось, не уверен что он сейчас точно показывает все возникшие ситуации, но ориентироваться на на него можно в большинстве случаев.

Re: Переполнение памяти во время работы макроса

1) Set ParamValueList = TCSApp.DocParams.Properties("VALUELIST").AsIDispatch
2) Set DOC_module=TCSApp.SingleDoc( CSDN_Const.dspSearchDocByVerID,Vib_Module.Properties("PRJVER_ID").AsInteger)
3) Set ATR_module = DOC_module.Properties("PARAMS_VALUES").AsIDispatch

С помощью .SingleInstance "Уничтожается ли внутренний объект TCS при освобождении интерфейса?" получаем:
1) ParamValueList .SingleInstance=False
2) DOC_module.SingleInstance=True
3) ATR_module.SingleInstance=False
Похоже вопрос надо понимать с точностью до наоборот?

Re: Переполнение памяти во время работы макроса

DOC_module.SingleInstance=True

По хорошему он говорит, что я глобальный и не уничтожусь (True). Хелп конечно надо тоже пересмотреть.

Re: Переполнение памяти во время работы макроса

Gaily!  :)

Re: Переполнение памяти во время работы макроса

Добрый день! Хочу поделиться новой проблемой на эту же тему.
К работающему фрагменту добавила команды удаления уничтожаемого объекта.
Уничтожаемый он или нет определяла с помощью Dob_Vers.SingleInstance.
В результате при повторе цикла на команде Dob_Vers.UserModuleName = "Dob_Vers_UniqueName" появляется ошибка «Требуется объект: ‘Dob_Vers’»

'Проверка, есть ли версия ТП
Call TCSApp.DeleteModuleByUserModuleName("Dob_Vers_UniqueName")    '1.1
Set Module_1 = TCSActiveModule.Properties("Versions").AsIDispatch
If  Not Module_1 Is Nothing Then 
  Call Module_1.First
  Do While Module_1.eof
     If TCSApp.MessageBoxAskYesNo(  "Подтвердите","Нет версии ТП"+chr(13)+chr(13)+"Добавить версию ТП?", False, 1,  False) =1 Then
        Set Dob_Vers = Module_1.ActionList.ActionByName("AppendAction")
        If  Not Dob_Vers Is Nothing Then Dob_Vers.Execute 
          Dob_Vers.UserModuleName = "Dob_Vers_UniqueName"                    '1.2
          Call TCSApp.DeleteModuleByUserModuleName("Dob_Vers_UniqueName")    '1.3
          Set Dob_Vers = Nothing
     Else
          Call TCSApp.ShowMessageBox("Нет версии ТП", "Прерывание макроса" )
          Exit Sub
     End If
     Module_1.Refresh
     Call Module_1.First
  Loop
End If

В аналогичном примере в циклом точно такая же проблема  с объектом

Set VerRigths = TCSActiveModule.ActionList.ActionByName("VerRigthsAction") 

Посоветуйте, пожалуйста.

Re: Переполнение памяти во время работы макроса

Да уж. Даже не знаю что сказать. В принципе вы правы, действительно такой код возможен, хотя тут видимо как раз вы и нарвались на нюансы этого SIngleInstance  (я писал что он в процессе  разработки свой смысл потерял, хотя видимо мало сконцентрировал на этом внимание в этот раз).

Вообще операции с UserModuleName  предполагалось производить в основном с модулями (IDModule и выше), хотя то что у вас написано в принципе правильно, но нюансы работы не подскажу, так как особого смысла в таких действия нет.

Вообще не рекомендую пользоваться без надобности UserModuleName, так как вы можете только хуже сделать.
Это нужно только для того чтобы насильно уничтожить глобальный объект или специально сделать глобальным неуничтожаемым локальный объект. ICSDNAction нет смысла существовать глобально без своего модуля, так что зачем так делать мне не ясно, и я думаю излишне.

Не забывайте так же что объекты ChildModules, ActionList  и все полученно от них сильно специфично для версии TechnologiCS и могут всегда измениться, в отличии от стандартных интерфейсов которые мы стараемся максимально поддерживать.