Memória insuficiente ou insuficientes?
-
28-09-2019 - |
Pergunta
Estou trabalhando em um projeto em larga escala, onde uma estrutura personalizada (muito boa e robusta) foi fornecida e temos que usá -lo para exibir formas e visualizações.
Existe a Classe Straticizadora de classe abstrata (derivada de alguma classe na estrutura) que é instanciada sempre que uma nova forma de estratégia é aberta.
StrategyForm
(um quadro de janela personalizado) contém StrategyEditor
.
StrategyEditor
contém StrategyTab
.
StrategyTab
contém StrategyCanvas
.
Esta é uma pequena parte das grandes classes para esclarecer que existem muitos objetos que serão criados se um objeto Strategyform for alocado na memória no tempo de execução. Meu componente possui todas essas classes mencionadas acima, exceto StrategyForm
cujo código não está sob meu controle.
Agora, em tempo de execução, o usuário abre muitos objetos de estratégia (que acionam a criação de um novo objeto Strategform.) Depois de criar aprox. 44 Objetos de estratégia, vemos que o objeto do usuário manipula (usarei o UOH daqui em diante) criado pelo aplicativo atinge cerca de 20k+, enquanto no registro o valor padrão das alças é 10K. Leia mais sobre os objetos do usuário aqui. O teste em diferentes máquinas deixou claro que o número de objetos de estratégia abertos é diferente para a mensagem para aparecer - em um M/C se tiver 44, pode ser 40 em outro.
Quando vemos a mensagem pop-up, isso significa que o aplicativo responderá lentamente. Piora com mais alguns objetos e, em seguida, a criação de quadros de janela e objetos subsequentes falham.
Primeiro, pensamos que não era uma questão de memória. Mas então lendo mais sobre new
em C# Ajudou a entender que uma exceção seria lançada se o aplicativo acabasse sem memória. Este não é um problema de memória, então, eu acho que (o gerenciador de tarefas também mostrou 1,5 GB de memória disponível.)
Especificações m/c
Núcleo 2 duo 2GHz+
4 GB de RAM
80 GB+ espaço de disco livre para arquivo de página
Conjunto de memória virtual: 4000 - 6000
Minhas perguntas
Q1. Isso parece um problema de memória e estou errado que não seja?
Q2. Isso ponto para a exaustão de UOHs gratuitos (como estou pensando) e que resultando na falha da criação de alças de janelas?
Q3. Como podemos evitar carregar um StrategyEditor
Objeto (além de um limiar, ficando de olho no uso atual do UOHS)? (Já sabemos como buscar o número de UOHs em uso, então não vá para lá.) Lembre -se de que o chamado para new StrategyForm()
está fora do controle do meu componente.
Q4. Estou um pouco confuso - o que são Lida com objetos de usuário exatamente? O MSDN está falando sobre qualquer objeto que criamos ou apenas alguns objetos específicos, como alças de janela, alças do cursor, alças de ícone?
Q5. O que exatamente faz com que use um UOH? (quase o mesmo que Q4)
Eu ficaria muito grato a quem pode me dar algumas respostas experientes. Muito obrigado! :)
Atualizar
Com base na resposta STAKX, observe que as janelas que estão sendo abertas serão fechadas apenas pelo usuário. Esta é a espécie de situação do aplicativo MDI, onde muitas crianças são abertas. Então, Dispose
Não pode ser chamado sempre que queremos.
Solução
Q1
Parece que você está tentando criar muitos controles de interface do usuário ao mesmo tempo. Mesmo que restem a memória, você está ficando sem alças. Veja abaixo uma explicação breve, mas bastante técnica.
Q4
Eu entendo um objeto de usuário ser qualquer objeto que faça parte da GUI. Pelo menos até o Windows XP, a API da interface do Windows residiu em USER.DLL
(uma das principais DLLs que compõem o Windows). Basicamente, a interface do usuário é composta de "Windows". Todos os controles, como botões, caixas de texto, caixas de seleção, são internamente a mesma coisa, a saber, "Windows". Para criá -los, você chamaria a função da API Win32 CreateWindow
. Essa função retornaria um identificador à "janela" criada (elemento da interface do usuário ou "objeto de usuário").
Então eu presumo que um identificador de objeto do usuário é uma alça conforme retornado por esta função. (WinForms é baseado na antiga API Win32 e, portanto, usaria o CreateWindow
função.)
Q2
Na verdade, você não pode criar tantos controles da interface do usuário quanto quiser. Todas essas alças recuperadas através CreateWindow
em algum momento deve ser libertado. Em Winforms, a maneira mais fácil e segura de fazer isso é através do uso do using
Bloco ou ligando Dispose
:
using (MyForm form = new MyForm())
{
if (form.ShowDialog() == DialogResult.OK) ...
}
Basicamente, tudo System.Windows.Forms.Control
pode ser Dispose
d, e deve ser descartado. Às vezes, isso é feito para você automaticamente, mas você não deve confiar nisso. Sempre Dispose
Sua interface do usuário controla quando você não precisa mais deles.
Nota em Dispose
Para formulários modais e modificados:
- Formas modais (mostradas com
ShowDialog
) são não descartado automaticamente. Você precisa fazer isso sozinho, como demonstrado no exemplo de código acima. - Formas modificadas (mostradas com
Show
) são descartados automaticamente para você, pois você não tem controle sobre quando será fechado pelo usuário. Não há necessidade de ligar explicitamenteDispose
!
Q5
Toda vez que você cria um objeto de interface do usuário, o WinForms faz chamadas internamente para CreateWindow
. É assim que as alças são alocadas. E eles não são libertados até uma chamada correspondente para DestroyWindow
é feito. Nas formas de win, essa chamada é desencadeada através do Dispose
método de qualquer System.Windows.Forms.Control
. (Nota: Embora eu esteja bem certo sobre isso, estou realmente adivinhando um pouco. Posso não estar 100% correto. Dando uma olhada nos internos do Winforms usando o refletor, revelaria a verdade.)
Q3
Assumindo que você StrategyEditor
Cria um enorme grupo de controles da interface do usuário, acho que você não pode fazer muito. Se você não pode simplificar esse controle (com relação ao número de controles infantis que ele cria), parece que você está preso na situação em que está. Você simplesmente não pode Crie infinitamente muitos controles da interface do usuário.
Você poderia, no entanto, acompanhar quantos StrategyEditor
s são abertos a qualquer momento (aumente um contador sempre que se for instanciado e diminui -o sempre que se fechar - você pode rastrear o último usando o FormClosing
/FormClosed
evento de uma forma, ou no Dispose
método de controle). Então você pode limitar o número de abertos simultaneamente StrategyEditor
s para um número fixo, digamos 5. Se o limite for excedido, você poderá lançar uma exceção no construtor, para que não sejam criadas mais instâncias. Claro que não posso dizer se StrategyForm
vai lidar com uma exceção do seu StrategyEditor
Construtor bem ...
public class StrategyEditor : ...
{
public StrategyEditor()
{
InitializeComponent();
if (numberOfLiveInstances >= maximumAllowedLiveInstances)
throw ...;
// not a nice solution IMHO, but if you've no other choice...
}
}
Em ambos os casos, limitando o número de instanciados StrategyEditor
S parece ser uma correção temporária para mim e não resolverá o problema real.