2010-07-26 5 views
3

J'ai une application qui effectue une grande quantité de calculs sur un tableau de nombres. Je l'ai mis à 100 000 opérations pour afficher le statut de ces chiffres. Ceci est juste une sorte d'indicateur visuel pour l'opérateur que l'application est encore en cours de traitement, et à quel point il est proche de l'achever. Après l'exécution de l'application pendant plusieurs heures, il se bloque avec une exception OutOfMemoryException. J'ai couru CLRProfiler sur lui, et a constaté que la fuite se produit dans le TextBox qui montre l'état actuel du tableau ... l'application construit ~ 4K dans Char [] chaque fois que l'interface met à jour, la trace de pile pour chaque morceau la mémoire est la même.TextBox.Text Fuite de mémoire dans l'application WPF

Est-ce un bug dans WPF, ou y a-t-il quelque chose que je ne sais pas que je devrais faire pour éviter que cela ne se produise? Je n'ai pas vu d'autres références à cela. Comme il s'agit quand même d'une opération assez gourmande en processeur, je préférerais ne pas avoir à détruire et à reconstruire le TextBox si je n'ai pas besoin de simplement afficher l'état actuel du tableau. C'est la seule fuite de mémoire que je peux trouver, mais en raison du nombre d'opérations nécessaires, c'est vraiment un gros problème pour l'application, même si je réduis simplement la fréquence de la mise à jour de l'interface. Si vous avez besoin de plus d'informations, je vous aiderai dans la mesure du possible, mais veuillez comprendre qu'en raison de ce que fait cette application, je ne peux pas publier de code source, et je vais devoir tout anonymiser (en supprimant toute indication l'application) que je peux poster. Lorsqu'il est exécuté à travers le profileur CLR, ce qui réduit drastiquement les performances, la trace identique suivante apparaît toutes les 2 secondes (rappelez-vous: 100 000 opérations) et la mémoire n'est jamais libérée. Lorsqu'il est exécuté sans profileur, l'intervalle entre les mises à jour est < 1 seconde.

La trace d'une mise à jour ("Qui affecté") est:

<root> : 3.7 kB (100.00%) 
WpfApplication0.App::Main static void(): 3.7 kB (100.00%) 
    System.Windows.Application::Run int32(): 3.7 kB (100.00%) 
    System.Windows.Application::Run int32 (System.Windows.Window): 3.7 kB (100.00%) 
    System.Windows.Application::RunInternal int32 (System.Windows.Window): 3.7 kB (100.00%) 
    System.Windows.Application::RunDispatcher Object (Object): 3.7 kB (100.00%) 
     System.Windows.Threading.Dispatcher::Run static void(): 3.7 kB (100.00%) 
     System.Windows.Threading.Dispatcher::PushFrame static void (System.Windows.Threading.DispatcherFrame): 3.7 kB (100.00%) 
     System.Windows.Threading.Dispatcher::PushFrameImpl void (System.Windows.Threading.DispatcherFrame): 3.7 kB (100.00%) 
     NATIVE FUNCTION (UNKNOWN ARGUMENTS): 3.7 kB (100.00%) 
      MS.Win32.HwndSubclass::SubclassWndProc int_ptr (int_ptr int32 int_ptr int_ptr): 3.7 kB (100.00%) 
      System.Windows.Threading.Dispatcher::Invoke Object (System.Windows.Threading.DispatcherPriority Object): 3.7 kB (100.00%) 
      System.Windows.Threading.Dispatcher::InvokeImpl Object (System.Windows.Threading.DispatcherPriority Object bool): 3.7 kB (100.00%) 
      System.Windows.Threading.Dispatcher::WrappedInvoke Object (Object bool): 3.7 kB (100.00%) 
       System.Windows.Threading.ExceptionWrapper::TryCatchWhen Object (Object Object bool): 3.7 kB (100.00%) 
       System.Windows.Threading.ExceptionWrapper::InternalRealCall Object (Object bool): 3.7 kB (100.00%) 
       MS.Win32.HwndSubclass::DispatcherCallbackOperation Object (Object): 3.7 kB (100.00%) 
       MS.Win32.HwndWrapper::WndProc int_ptr (int_ptr int32 int_ptr int_ptr bool&): 3.7 kB (100.00%) 
        System.Windows.Threading.Dispatcher::WndProcHook int_ptr (int_ptr int32 int_ptr int_ptr bool&): 3.7 kB (100.00%) 
        System.Windows.Threading.Dispatcher::ProcessQueue void(): 3.7 kB (100.00%) 
        System.Windows.Threading.DispatcherOperation::Invoke Object(): 3.7 kB (100.00%) 
        System.Threading.ExecutionContext::Run static void (System.Threading.ExecutionContext System.Threading.ContextCallback Object): 3.7 kB (100.00%) 
         System.Threading.ExecutionContext::RunInternal static void (System.Threading.ExecutionContext System.Threading.ContextCallback Object): 3.7 kB (100.00%) 
         System.Threading.ExecutionContext::runTryCode static void (Object): 3.7 kB (100.00%) 
         System.Windows.Threading.DispatcherOperation::InvokeInSecurityContext static void (Object): 3.7 kB (100.00%) 
         System.Windows.Threading.DispatcherOperation::InvokeImpl void(): 3.7 kB (100.00%) 
          System.Windows.Threading.Dispatcher::WrappedInvoke(1) Object (Object bool): 3.7 kB (100.00%) 
          System.Windows.Threading.ExceptionWrapper::TryCatchWhen(1) Object (Object Object bool): 3.7 kB (100.00%) 
          System.Windows.Threading.ExceptionWrapper::InternalRealCall(1) Object (Object bool): 3.7 kB (100.00%) 
          System.Delegate::DynamicInvokeImpl Object (Object[]): 3.7 kB (100.00%) 
           System.Reflection.RuntimeMethodInfo::Invoke Object (Object System.Reflection.BindingFlags System.Reflection.Binder Object[] System.Globalization.CultureInfo bool): 3.7 kB (100.00%) 
           System.RuntimeMethodHandle::InvokeMethodFast Object (Object Object[] System.Signature System.Reflection.MethodAttributes System.RuntimeTypeHandle): 3.7 kB (100.00%) 
           WpfApplication0.Window1::UpdateUI void(): 3.7 kB (100.00%) 
           System.Windows.Controls.TextBox::set_Text void (String): 3.7 kB (100.00%) 
            System.Windows.DependencyObject::SetValue void (System.Windows.DependencyProperty Object): 3.7 kB (100.00%) 
            System.Windows.DependencyObject::SetValueCommon void (System.Windows.DependencyProperty Object System.Windows.PropertyMetadata bool System.Windows.OperationType bool): 3.7 kB (100.00%) 
            System.Windows.DependencyObject::UpdateEffectiveValue System.Windows.UpdateResult (System.Windows.EntryIndex System.Windows.DependencyProperty System.Windows.PropertyMetadata System.Windows.EffectiveValueEntry System.Windows.EffectiveValueEntry& bool System.Windows.OperationType): 3.7 kB (100.00%) 
            System.Windows.DependencyObject::NotifyPropertyChange void (System.Windows.DependencyPropertyChangedEventArgs): 3.7 kB (100.00%) 
             System.Windows.Controls.TextBox::OnPropertyChanged void(): 3.7 kB (100.00%) 
             System.Windows.FrameworkElement::OnPropertyChanged void(): 3.7 kB (100.00%) 
             System.Windows.DependencyObject::OnPropertyChanged void (System.Windows.DependencyPropertyChangedEventArgs): 3.7 kB (100.00%) 
             System.Windows.Controls.TextBox::OnTextPropertyChanged static void (): 3.7 kB (100.00%) 
              System.Windows.Documents.TextContainer::DeleteContentInternal void (System.Windows.Documents.TextPointer System.Windows.Documents.TextPointer): 3.7 kB (100.00%) 
              System.Windows.Documents.TextTreeUndo::CreateDeleteContentUndoUnit static System.Windows.Documents.TextTreeDeleteContentUndoUnit (System.Windows.Documents.TextContainer System.Windows.Documents.TextPointer System.Windows.Documents.TextPointer): 3.7 kB (100.00%) 
              System.Windows.Documents.TextTreeDeleteContentUndoUnit::.ctor void (System.Windows.Documents.TextContainer System.Windows.Documents.TextPointer System.Windows.Documents.TextPointer): 3.7 kB (100.00%) 
              System.Windows.Documents.TextTreeDeleteContentUndoUnit::CopyContent ContentContainer (System.Windows.Documents.TextTreeNode System.Windows.Documents.TextTreeNode): 3.7 kB (100.00%) 
               System.Windows.Documents.TextTreeDeleteContentUndoUnit::CopyTextNode System.Windows.Documents.TextTreeNode (System.Windows.Documents.TextTreeTextNode System.Windows.Documents.TextTreeNode ContentContainer&): 3.7 kB (100.00%) 
               System.Char [] : 3.7 kB (100.00%) 

et le code à générer une mise à jour de l'interface utilisateur est:

 List<int> arraystatus = new List<int>(displayarray.ToArray()); 

     StringBuilder s = new StringBuilder(); 

     int i = 1; 

     foreach (int item in arraystatus) 
     { 
      s.Append(i.ToString() + ":\t" + item.ToString() + (i % 8 == 0 ? "\n" : "\t")); 
      i++; 
     } 

     txtStatus.Text = s.ToString(); 

     arraystatus = null; 
     s = null; 
+1

Est-ce un bug dans WPF? 99% sûr non. Est-ce que vous allouer des craptons de chaînes? Pourrait être, pas sûr de votre code. Y a-t-il un meilleur moyen? Probablement. – Will

+0

En outre, la mémoire n'est généralement pas libérée lors du débogage; est-ce que cela se produit pendant la diffusion? – Will

+0

C'est littéralement le seul bit de code qui crée une chaîne après la création initiale de l'interface utilisateur. Il est encapsulé dans une méthode invoquée par le répartiteur depuis mon thread de travail toutes les 100 000 opérations. Oui, c'est beaucoup de ficelles. Ma question est "Pourquoi ne partent-ils pas?". Et oui, cela arrive dans la version ou le débogage. –

Répondre

17

De la documentation de la propriété TextBoxBase.UndoLimit:

Le nombre d'actions stockées dans la file d'attente d'annulation. La valeur par défaut est -1, ce qui signifie que la file d'attente d'annulation est limitée à la mémoire disponible.

Vous avez trouvé cette limite. Réglez-le sur une valeur raisonnablement petite.

Cela n'a normalement pas beaucoup de sens d'afficher les informations de journalisation dans un TextBox. Il est vraiment destiné à permettre à l'utilisateur d'entrer du texte. TextBlock est peut-être un meilleur choix. MISE À JOUR: dans .NET 4.5, la valeur par défaut de -1 a été changée à 100 pour éviter ce type d'utilisation de la mémoire en fugue.

+2

Excellente prise sur ça ... ça roule sans fuites maintenant. Merci! +1 et accepter. –

+0

@ md5sum FUITES ??? LEAKKKSSSS !!!!! Comment osez-vous, SIR! – Will

+0

* efface la failousce renversée * –

2

+1 pour Hans.

De plus, vous utilisez StringBuilder. Impressionnant. Cependant, vous concaténénez QUATRE chaînes pour chaque élément de arraystatus (ce qui peut être 100k pour tout ce que je sais) pour chaque mise à jour de votre interface utilisateur, rendant StringBuilder à peu près inutile.

Essayez

s.Append(i.ToString()); 
s.Append(":\t"); 
s.Append(item.ToString()); 
s.Append(i % 8 == 0 ? "\n" : "\t")); 

ou, mieux encore, essayez de le convertir à un seul appel de AppendFormat.

+0

Ouais, ma stupidité là, ne pensait pas pendant que j'écrivais apparemment un jour ... +1 –