27 апр. 2021 г.

NB! Об использовании TCreator и других VB классов

Важное предварительное замечание: отладчик Гедымина вносит существенные искажения в исходный код скрипт-функций из-за чего сборка мусора может работать не так, как ожидает этого программист, и не так, как она будет работать в режиме без отладочной информации. Всегда проверяйте код в режиме без отладки перед передачей в промышленную эксплуатацию.

Особенности классов VBScript

  1. Событие Terminate вызывается только в процессе удаления из памяти последнего экземпляра данного класса.
  2. Наличие кольцевых ссылок, в т.ч. цепочек кольцевых зависимостей любой длины, приведет к тому, что ни один из экземпляров не будет удален сборщиком мусора и останется в памяти до конца работы программы.

Как мы столкнулись с проблемой неудаления объектов из памяти

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

Возникает вопрос, как бороться с вышеуказанной частной ситуацией и как правильно работать с классами VBScript, чтобы максимально обезопасить себя от утечек памяти?

Использовать Designer.CreateObject -- Designer.DestroyObject

В описанном выше примере программист изначально понимает, что созданный экземпляр TCreator будет существовать до закрытия модального окна, а значит будет удерживать все последующие TCreator. В данном случае можно предложить решение с выделением и уничтожением ресурсов напрямую через глобальный объект Designer.

Создать копию класса TCreator

Вы автор подсистемы на платформе Гедымин. Код протестирован на корректную работу с ресурсами. Но, как обезопасить себя от ситуации, когда другая подсистема создаст долгоживущий TCreator и заблокирует все ваши механизмы очистки памяти? 

Следует создать полную копию класса TCreator в рамках своей подсистемы и использовать ее для выделения ресурсов.

А надо ли каждый раз выделять-уничтожать ресуры?

В указанном выше приложении сотни раз за рабочую смену выделялись и уничтожались одни и те же ресурсы -- транзакции и запросы к базе данных. Одно из решений -- пул ресурсов. Нужные объекты создаются единожды и привязываются к окну (у нас есть соответствующее свойство Objects у формы). Остается только стартовать/комитить транзакции в нужных местах и выполнять/закрывать запросы к базе данных, без уничтожения самих объектов. 

Альтернативный вариант в случае с экранной формой -- не создавать объекты для пула из программного кода, а просто разместить соответствующие компоненты на форме и обращаться к ним через метод GetComponent.

Принудительно уничтожить ресурсы в TCreator

В крайнем случае, ресуры выделенные через TCreator можно уничтожить принудительно, вызвав метод DestroyAllObjects. Теперь, даже если сборщик мусора не сможет удалить экземпляр TCreator, то в памяти останется только он, но не связанные с ним ресурсы.

Уничтожать TCreator как можно раньше

Стандартная практика -- TCreator уничтожается по завершении функции или процедуры. Если код процедуры или функции объемный и ресурсы нужны только в одной его части, то рекомендуется уничтожать экземпляр TCreator вручную, присваивая переменной значение Nothing.

...
Dim Creator
Set Creator = New TCreator
...
здесь работаем с ресурсами
...
больше нам выделенные ресурсы не нужны
Set Creator = Nothing
...
продолжается код функции/процедуры
...

Комментариев нет:

Отправить комментарий