Недостаточно памяти или недостаточно ручек? - PullRequest
1 голос
/ 20 апреля 2010

Я работаю над крупномасштабным проектом, в котором была предоставлена ​​пользовательская (довольно хорошая и надежная) инфраструктура, и мы должны использовать ее для отображения форм и представлений.


Существует абстрактный класс StrategyEditor (производный от некоторого класса в платформе), который создается при каждом открытии новой StrategyForm.

StrategyForm (настраиваемая оконная рама) содержит StrategyEditor.
StrategyEditor содержит StrategyTab.
StrategyTab содержит StrategyCanvas.

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


Теперь во время выполнения пользователь открывает много объектов стратегии (которые инициируют создание нового объекта StrategyForm.) После создания прибл. 44 объекта стратегии, мы видим, что РУЧКИ ОБЪЕКТА ПОЛЬЗОВАТЕЛЯ (я буду использовать UOH и далее), созданные приложением, достигают примерно 20k +, в то время как в реестре количество по умолчанию для дескрипторов составляет 10k. Подробнее о пользовательских объектах читайте здесь. Тестирование на разных машинах показало, что число открываемых объектов стратегии различно для всплывающего сообщения - на одном т / с, если оно равно 44, тогда оно может быть 40 на другом.

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

Сначала мы подумали, что это нехватка памяти. Но тогда , читая больше о new в C # , помогло понять, что будет выброшено исключение, если приложению не хватит памяти. Мне кажется, это не проблема с памятью (диспетчер задач также показал 1,5 ГБ + доступную память.)


Характеристики M / C
Core 2 Duo 2 ГГц +
4 ГБ ОЗУ
80 ГБ + свободное место на диске для файла подкачки
Набор виртуальной памяти: 4000 - 6000


Мои вопросы


Q1. Это похоже на проблему с памятью, и я ошибаюсь, что это не так?
Q2. Указывает ли это на исчерпание свободных UOH (как я думаю) и что приводит к невозможности создания дескрипторов окон?
Q3. Как мы можем избежать загрузки объекта StrategyEditor (за порогом, следя за текущим использованием UOH)? (мы уже знаем, как получить количество используемых UOH, поэтому не ходите туда.) Имейте в виду, что вызов new StrategyForm() находится вне контроля моего компонента.
Q4. Я немного растерялся - что такое Дескрипторы пользовательских объектов точно? Говорит ли MSDN о каком-либо объекте, который мы создаем, или только о некоторых конкретных объектах, таких как дескрипторы окон, маркеры курсоров, маркеры значков?
Q5. Что именно вызывает использование UOH? (почти так же, как Q4)

Я был бы очень благодарен любому, кто может дать мне несколько знающих ответов. Большое спасибо! :)

[Update]
Основываясь на ответе Stakx, обратите внимание, что открываемые окна будут закрыты только для пользователя. Это своего рода ситуация с приложением MDI, когда открывается слишком много дочерних окон. Таким образом, Dispose не может быть вызван, когда мы хотим.

1 Ответ

2 голосов
/ 20 апреля 2010

Q1

Звучит так, будто вы пытаетесь создать слишком много элементов управления пользовательского интерфейса одновременно. Даже если память осталась, у тебя кончились ручки. Ниже приведено краткое, но довольно техническое объяснение.

Q4

Я понимаю, что пользовательский объект - это любой объект, который является частью GUI. По крайней мере, до Windows XP API пользовательского интерфейса Windows находился в USER.DLL (одна из основных DLL, составляющих Windows). По сути, пользовательский интерфейс состоит из «окон». Все элементы управления, такие как кнопки, текстовые поля, флажки, внутренне одно и то же, а именно «окна». Чтобы создать их, вы должны вызвать функцию Win32 API CreateWindow. Эта функция затем возвращает дескриптор созданного «окна» (элемент пользовательского интерфейса или «пользовательский объект»).

Итак, я предполагаю, что дескриптор пользовательского объекта является дескриптором, возвращаемым этой функцией. (Winforms основан на старом Win32 API и поэтому будет использовать функцию CreateWindow.)

Q2

Действительно, вы не можете создать столько элементов управления пользовательским интерфейсом, сколько захотите. Все эти дескрипторы, полученные через CreateWindow, должны в какой-то момент быть освобождены. В Winforms самый простой и безопасный способ сделать это - использовать блок using или вызвать Dispose:

using (MyForm form = new MyForm())
{
    if (form.ShowDialog() == DialogResult.OK) ...
}    

Как правило, все System.Windows.Forms.Control могут быть Dispose d и должны быть утилизированы. Иногда это делается автоматически, но вам не следует полагаться на это. Всегда Dispose контролирует ваш пользовательский интерфейс, когда он вам больше не нужен.

Примечание по Dispose для модальных и немодальных форм:

  • Модальные формы (показаны ShowDialog) не автоматически выбрасываются. Вы должны сделать это самостоятельно, как показано в примере кода выше.
  • Немодальные формы (показанные с Show) автоматически удаляются для вас, поскольку вы не можете контролировать, когда они будут закрыты пользователем. Нет необходимости явно звонить Dispose!

1049 * Q5 * Каждый раз, когда вы создаете объект пользовательского интерфейса, Winforms внутренне выполняет вызовы CreateWindow. Вот как распределяются ручки. И они не освобождаются, пока не будет сделан соответствующий вызов DestroyWindow. В Winforms этот вызов запускается с помощью метода Dispose любого System.Windows.Forms.Control. (Примечание. Хотя я в этом достаточно уверен, на самом деле я немного догадываюсь. Возможно, я не на 100% прав. Если взглянуть на внутренности Winforms с помощью Reflector, это откроет правду.) Q3

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

Однако вы можете отслеживать, сколько StrategyEditor с открыто одновременно (увеличивать счетчик всякий раз, когда создаете экземпляр, и уменьшать его всякий раз, когда закрываете - вы можете отслеживать последний, используя FormClosing / FormClosed событие формы или метод управления Dispose). Тогда вы можете ограничить число одновременно открытых StrategyEditor s фиксированным числом, скажем, 5. Если предел превышен, вы можете вызвать исключение в конструкторе, чтобы больше не создавалось экземпляров. Конечно, я не могу сказать, будет ли StrategyForm хорошо обрабатывать исключение из вашего StrategyEditor конструктора ...

public class StrategyEditor : ...
{
    public StrategyEditor()
    {
        InitializeComponent();

        if (numberOfLiveInstances >= maximumAllowedLiveInstances)
            throw ...;
        // not a nice solution IMHO, but if you've no other choice...
    }
}

В любом случае ограничение числа экземпляров StrategyEditor кажется временным исправлением и не решит настоящую проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...