none
Alto consumo de memória RRS feed

  • Pergunta

  • Bom dia,

    Estou desenvolvendo uma aplicação em WPF que tem a funcionalidade de realizar backup/restore em bases Firebird. É uma aplicação relativamente pequena, com apenas 3 opções de execução, 1 de configuração e 1 de log. O aplicativo trabalha com ADO.NET, com provider para Firebird, utiliza recursos de FTP, compactação e descompactação e executa processos em background. 

    Na parte visual, estou utilizando um tema (ReuxablesLegacy.dll) e uma imagem de fundo em todas as pages (PNG de 60Kb), definida através de um style aplicado nestas pages. Em apenas 1 das páginas utilizo o recurso de opacidade, que já li que não é muito recomendado, justamente por conta de consumo de memória.

    Rodando a aplicação em um Windows 7 (com todos os updates e services pack) a aplicação só no start chega a consumir 113Mb de memória. Conforme as opções vão sendo utilizadas, a memória vai acumulando, e chegou ao montante de 513Mb.

    No geral a aplicação possui um bom desempenho, mas me preocupa a quantidade de memória sendo consumida e que pode dificultar a utilização em máquinas com menos recursos.

    Alguém passou por mesma experiência ou possui dicas para auxiliar?

    Grato,

    Rodrigo 

    quinta-feira, 10 de março de 2011 13:34

Respostas

  • Pela descrição da aplicação não deveria consumir tanto, desenvolvo sistemas em WPF com charts de controle no Startup e consomem 30 mb no máximo, recomendo utilizar o Profiler do Visual Studio para identificar mais rapidamente seu problema (Visual Studio Ultimate/Visual Studio Premium). Caso não use essas versões me diga o que você faz ao carregar a aplicação?

    Link que pode te auxiliar:

    http://msdn.microsoft.com/en-us/library/ms182376.aspx


    Atenciosamente, Paulo R. Pereira de Souza
    http://paulosouza.net
    E-mail: paulorpereirasouza@hotmail.com. twitter facebook linkedin
    quinta-feira, 10 de março de 2011 19:21
  • Ola

     

    Vc esta usando em algun item de sua aplicação o efeito DropShadowEffect?

    Retire esse tratamento e não apresentara mais o problema de performance

     

    sexta-feira, 11 de março de 2011 13:22
  • Rodrigo,

    Uma coisa que você poderia tentar pra ver se melhora é setar para 0 o UndoLimit do seu TextBox...


    André Alves de Lima
    Microsoft MVP - Client App Dev
    Visite o meu site: http://www.andrealveslima.com.br
    Me siga no Twitter: @andrealveslima
    sexta-feira, 11 de março de 2011 14:41
    Moderador
  • Rodrigo,

    Tente não trabalhar diretamente com o TextBox.AppendText, por que você não trabalha com vinculação de dados (DataBinding) verifique o pattern MVVM é ótimo para trabalhar com WPF, acredito que terá uma melhor performance, na vinculação de dados utilize um objeto StringBuilder para ir adicionado as mensagens de evento. Algo desse tipo:

    private StringBuilder _log;
      public string Log 
      {
       get
       {
        if (_log == null)
         _log = new StringBuilder(); 
        return _log.ToString();
       }
       set 
       { if (_log == null)
          _log = new StringBuilder(); 
        _log.Append(value);
       }
      }

    Outra coisa, caso a notificação de eventos (Log) seja algo muito rapido, como varias vezes por segundo, você não irá querer executar o método ScrollToEnd nesta velocidade, o usuário não iria conseguir fazer a leitura :), então esse método pode ser executado a cada 3-5 segundos. Utilize um objeto System.Threading.Timer para o agendamento deste método.


    Atenciosamente, Paulo R. Pereira de Souza
    http://paulosouza.net
    E-mail: paulorpereirasouza@hotmail.com. twitter facebook linkedin
    domingo, 13 de março de 2011 23:57
  • De uma olhada no artigo que escrevi sobre a utilização do objeto BackgroundWorker, WPF, MVVM, no link: http://paulosouza.net/Article/Details/2 , pode te auxiliar.

    Exemplo de aplicação:

    <Window x:Class="WpfAsync.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow"
            Height="350"
            Width="525">
        <Grid>
            <TextBox Name="textBoxLog"
                     Text="{Binding Log}"
                     Height="129"
                     HorizontalAlignment="Left"
                     TextWrapping="Wrap"
                     Margin="12,12,0,0"
                     VerticalAlignment="Top"
                     Width="479" />
            <Button Name="button1"
                    Command="{Binding ProcessUpdateCommand}"
                    Content="Button"
                    Height="23"
                    HorizontalAlignment="Left"
                    Margin="12,147,0,0"
                    VerticalAlignment="Top"
                    Width="75" />
        </Grid>
    </Window>
    
    namespace WpfAsync
    {
        public class MainWindowViewModel : AsyncPresentationModel
        {
            public RelayCommand ProcessUpdateCommand
            {
                get
                {
                    if (_processUpdateCommand == null)
                    {
                        _processUpdateCommand = new RelayCommand(
                            param =>
                            {
                                this.ProcessAsync((sender, e) =>
                                {
                                    var worker = sender as BackgroundWorker;
                                    worker.WorkerReportsProgress = true;
                                    worker.ProgressChanged += (sender1, e1) => 
                                    {
                                        this.NotifyPropertyChanged<string>(() => this.Log);
                                    };
     
                                    for (int i = 0; i < 100; i++) 
                                    {
                                        Log = string.Format("Atualização {0}", i);
                                        Thread.Sleep(TimeSpan.FromSeconds(0.5));
                                        worker.ReportProgress(i);
                                    }
                                },
                                (sender, e) =>
                                {
                                    this.NotifyPropertyChanged<string>(() => this.Log);
                                },
                                "Atualizando...");
                            },
                            param => true);
                    }
                    return _processUpdateCommand;
                }
            }
            private RelayCommand _processUpdateCommand;
     
            public string Log
            {
                get
                {
                    if (_log == null)
                        _log = new StringBuilder();
                    return _log.ToString();
                }
                set
                {
                    if (_log == null)
                        _log = new StringBuilder();
                    _log.Append(value);
                }
            }
            private StringBuilder _log;
        }
    }
    De uma olhanda no CodePlex pelo componente WPFToolkit.Extended tem alguns objetos legais.
    Atenciosamente, Paulo R. Pereira de Souza
    http://paulosouza.net
    E-mail: paulorpereirasouza@hotmail.com. twitter facebook linkedin
    segunda-feira, 14 de março de 2011 17:57

Todas as Respostas

  • Pela descrição da aplicação não deveria consumir tanto, desenvolvo sistemas em WPF com charts de controle no Startup e consomem 30 mb no máximo, recomendo utilizar o Profiler do Visual Studio para identificar mais rapidamente seu problema (Visual Studio Ultimate/Visual Studio Premium). Caso não use essas versões me diga o que você faz ao carregar a aplicação?

    Link que pode te auxiliar:

    http://msdn.microsoft.com/en-us/library/ms182376.aspx


    Atenciosamente, Paulo R. Pereira de Souza
    http://paulosouza.net
    E-mail: paulorpereirasouza@hotmail.com. twitter facebook linkedin
    quinta-feira, 10 de março de 2011 19:21
  • Obrigado pela resposta Paulo.

    Um dos motivos de tanto consumo de memória consegui identificar. A minha aplicação estava sendo monitorada pelo Application Verifier. Após desabilitar o monitoramento um montante desta memória deixou de ser alocada.

    Estava desconfiado dos recursos visuais da aplicação, mas removi tema, imagens e mesmo assim a memória continuou alta.

    Iniciei a análise da aplicação através do Profiler como você recomendou. Vamos ver o que identificarei de problema.

    Encontrando alguma coisa postarei aqui no forum.

    Grato,

    Rodrigo 

    quinta-feira, 10 de março de 2011 20:24
  • Ola

     

    Vc esta usando em algun item de sua aplicação o efeito DropShadowEffect?

    Retire esse tratamento e não apresentara mais o problema de performance

     

    sexta-feira, 11 de março de 2011 13:22
  • Obrigado pela resposta Rafael.

    Em minha aplicação não utilizo este recurso, e também não tenho nenhum controle com AllowsTransparency habilitado. O máximo que utilizo é modificar a propriedade Opacity de algumas imagens para dar um efeito de desabilitado (tenho botões com imagens dentro, quando executo enable=false no botão, a imagem continua da mesma forma, então modifico via código a propriedade opacity).

    Grato,

    Rodrigo

    sexta-feira, 11 de março de 2011 13:52
  • Bom dia Paulo,

    Agradeço pela dica sobre o Profiler, ótima ferramenta do Visual Studio. Através dela consegui identificar dois grandes gargalos na aplicação, causadores de um alto consumo de memória, explico: como o objetivo da aplicação é realizar backup/restore de banco de dados, no caso do Firebird, executo nativamente o aplicativo de linha de comando através de CreateProcess. Durante a execução capturo a saída da linha de comando e exibo para o usuário dentro de um TextBox, acumulando cada linha exibida, o que no final me gera um log de como o processo foi executado. Para adicionar o texto ao TextBox executo o método AppendText (este é o primeiro gargalo), mas o texto adicionado não é exibido para o usuário, porque são muitas linhas, então logo em seguida executo o método ScrollToEnd (este é o segundo gargalo). Os dois métodos juntos, ao final do processo totalizam 78Mb.

    Após esta longa descrição pergunto, existe um método menos custoso para informar o usuário do que está acontecendo?

    Grato,

    Rodrigo

    sexta-feira, 11 de março de 2011 14:05
  • Rodrigo,

    Uma coisa que você poderia tentar pra ver se melhora é setar para 0 o UndoLimit do seu TextBox...


    André Alves de Lima
    Microsoft MVP - Client App Dev
    Visite o meu site: http://www.andrealveslima.com.br
    Me siga no Twitter: @andrealveslima
    sexta-feira, 11 de março de 2011 14:41
    Moderador
  • Rodrigo,

    Tente não trabalhar diretamente com o TextBox.AppendText, por que você não trabalha com vinculação de dados (DataBinding) verifique o pattern MVVM é ótimo para trabalhar com WPF, acredito que terá uma melhor performance, na vinculação de dados utilize um objeto StringBuilder para ir adicionado as mensagens de evento. Algo desse tipo:

    private StringBuilder _log;
      public string Log 
      {
       get
       {
        if (_log == null)
         _log = new StringBuilder(); 
        return _log.ToString();
       }
       set 
       { if (_log == null)
          _log = new StringBuilder(); 
        _log.Append(value);
       }
      }

    Outra coisa, caso a notificação de eventos (Log) seja algo muito rapido, como varias vezes por segundo, você não irá querer executar o método ScrollToEnd nesta velocidade, o usuário não iria conseguir fazer a leitura :), então esse método pode ser executado a cada 3-5 segundos. Utilize um objeto System.Threading.Timer para o agendamento deste método.


    Atenciosamente, Paulo R. Pereira de Souza
    http://paulosouza.net
    E-mail: paulorpereirasouza@hotmail.com. twitter facebook linkedin
    domingo, 13 de março de 2011 23:57
  • Boa tarde Paulo, e novamente obrigado pela resposta.

    Conheço o pattern MVVM, mas tenho uma limitação neste processo. Ele está sendo executado através de um BackgroundWorker, para manter o status da aplicação como "Executando" e permitir ao usuário o cancelamento do processo. É através do evento DoWork que crio a instância de minha classe de Backup, e esta classe é quem possui a propriedade "Log", como sugerido.

    Acontece que através da thread criada pelo BackgroundWorker não tenho como modificar nenhum componente/classe da thread principal. O único meio de realizar a comunicação é através do evento ProgressChanged, no qual envio a nova linha de log e utilizo o AppendText do TextBox.

    Analisando o evento BackgroundWorker.ProgressChanged, é possível transferir um objeto de uma thread para outra. Não sei se por este canal conseguiria realizar DataBinding com o TextBox?!

    Grato,

    Rodrigo 

    segunda-feira, 14 de março de 2011 16:51
  • Boa tarde André, obrigado pela resposta.

    Realizei a alteração sugerida, mas não obtive resultado, continuando o mesmo consumo de memória. O componente TextBox estava configurado como ReadOnly, não sei se esta propriedade pode interferir na questão do Undo.

    Grato,

    Rodrigo.

    segunda-feira, 14 de março de 2011 17:08
  • De uma olhada no artigo que escrevi sobre a utilização do objeto BackgroundWorker, WPF, MVVM, no link: http://paulosouza.net/Article/Details/2 , pode te auxiliar.

    Exemplo de aplicação:

    <Window x:Class="WpfAsync.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow"
            Height="350"
            Width="525">
        <Grid>
            <TextBox Name="textBoxLog"
                     Text="{Binding Log}"
                     Height="129"
                     HorizontalAlignment="Left"
                     TextWrapping="Wrap"
                     Margin="12,12,0,0"
                     VerticalAlignment="Top"
                     Width="479" />
            <Button Name="button1"
                    Command="{Binding ProcessUpdateCommand}"
                    Content="Button"
                    Height="23"
                    HorizontalAlignment="Left"
                    Margin="12,147,0,0"
                    VerticalAlignment="Top"
                    Width="75" />
        </Grid>
    </Window>
    
    namespace WpfAsync
    {
        public class MainWindowViewModel : AsyncPresentationModel
        {
            public RelayCommand ProcessUpdateCommand
            {
                get
                {
                    if (_processUpdateCommand == null)
                    {
                        _processUpdateCommand = new RelayCommand(
                            param =>
                            {
                                this.ProcessAsync((sender, e) =>
                                {
                                    var worker = sender as BackgroundWorker;
                                    worker.WorkerReportsProgress = true;
                                    worker.ProgressChanged += (sender1, e1) => 
                                    {
                                        this.NotifyPropertyChanged<string>(() => this.Log);
                                    };
     
                                    for (int i = 0; i < 100; i++) 
                                    {
                                        Log = string.Format("Atualização {0}", i);
                                        Thread.Sleep(TimeSpan.FromSeconds(0.5));
                                        worker.ReportProgress(i);
                                    }
                                },
                                (sender, e) =>
                                {
                                    this.NotifyPropertyChanged<string>(() => this.Log);
                                },
                                "Atualizando...");
                            },
                            param => true);
                    }
                    return _processUpdateCommand;
                }
            }
            private RelayCommand _processUpdateCommand;
     
            public string Log
            {
                get
                {
                    if (_log == null)
                        _log = new StringBuilder();
                    return _log.ToString();
                }
                set
                {
                    if (_log == null)
                        _log = new StringBuilder();
                    _log.Append(value);
                }
            }
            private StringBuilder _log;
        }
    }
    De uma olhanda no CodePlex pelo componente WPFToolkit.Extended tem alguns objetos legais.
    Atenciosamente, Paulo R. Pereira de Souza
    http://paulosouza.net
    E-mail: paulorpereirasouza@hotmail.com. twitter facebook linkedin
    segunda-feira, 14 de março de 2011 17:57
  • Rodrigo,

    Conseguiu solucionar essa questão?


    André Alves de Lima
    Microsoft MVP - Client App Dev
    Visite o meu site: http://www.andrealveslima.com.br
    Me siga no Twitter: @andrealveslima
    sexta-feira, 13 de maio de 2011 18:22
    Moderador