2013-03-25 13 views
7

Ho il seguente codice, che genererà un OutOfMemoryException quando viene eseguito:Qual è l'alternativa a GarbageCollector?

public partial class MainWindow : Window 
{ 
    private DrawingVisual myVisual = new DrawingVisual(); 

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    private void Window_Loaded(object sender, RoutedEventArgs e) 
    { 
     myVisual = GetVisual(); 
     graphicsCanvas.AddVisual(myVisual); 
    } 

    private void Graphics_Canvas_MouseMove(object sender, MouseEventArgs e) 
    { 
     //get the data visual: 
     DrawingVisual tempVisual = GetVisual(); 

     //first clear the current display data: 
     graphicsCanvas.RemoveVisual(myVisual); 

     //get the data visual: 
     myVisual = tempVisual; 

     graphicsCanvas.AddVisual(myVisual); 

     //GC.Collect(); 
    } 

    private DrawingVisual GetVisual() 
    { 
     double width = graphicsCanvas.ActualWidth; 
     double height = graphicsCanvas.ActualHeight; 

     DrawingVisual dV = new DrawingVisual(); 

     Rect clipRect = new Rect(0, 0, width, height); 

     dV.Clip = new RectangleGeometry(clipRect); 

     using (DrawingContext dC = dV.RenderOpen()) 
     { 
      RenderTargetBitmap rTB = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Pbgra32); 

      if (rTB.CanFreeze) 
      { 
       rTB.Freeze(); 
      } 

      dC.DrawImage(rTB, clipRect); 
     } 

     return dV; 
    } 
} 

Qualora un Graphics_Canvas è definito come segue:

class Graphics_Canvas : Canvas 
{ 
    private List<DrawingVisual> visuals = new List<DrawingVisual>(); 

    protected override int VisualChildrenCount 
    { 
     get { return visuals.Count; } 
    } 

    protected override Visual GetVisualChild(int index) 
    { 
     return visuals[index]; 
    } 

    public void AddVisual(DrawingVisual visual) 
    { 
     visuals.Add(visual); 

     base.AddVisualChild(visual); 
     base.AddLogicalChild(visual); 
    } 

    public bool ContainsVisual(DrawingVisual visual) 
    { 
     return visuals.Contains(visual); 
    } 

    public bool HasVisuals 
    { 
     get { return visuals.Count > 0; } 
    } 

    public void RemoveAllVisuals() 
    { 
     for (int i = 0; i < visuals.Count; i++) 
     { 
      RemoveFromLogicalTree(visuals[i]); 
     } 

     visuals.Clear(); 
    } 

    private void RemoveFromLogicalTree(Visual visual) 
    { 
     RemoveLogicalChild(visual); 
     RemoveVisualChild(visual); 
    } 

    public void RemoveLastVisual() 
    { 
     if (visuals.Count > 0) 
     { 
      int index = visuals.Count - 1; 

      RemoveFromLogicalTree(visuals[index]); 
      visuals.Remove(visuals[index]); 
     }  
    } 

    public void RemoveVisual(DrawingVisual visual) 
    { 
     RemoveFromLogicalTree(visual); 
     visuals.Remove(visual);    
    } 
} 

E il codice XAML per creare il Window è come questo:

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:csMemoryLeakTestProject" x:Class="csMemoryLeakTestProject.MainWindow" 
    Title="MainWindow" 
    Background="Gray" 
    Height="600" Width="800" 
    WindowStartupLocation="CenterScreen" 
    Loaded="Window_Loaded"> 
    <Grid> 
     <local:Graphics_Canvas Margin="12" Background="White" x:Name="graphicsCanvas" MouseMove="Graphics_Canvas_MouseMove"/> 
    </Grid> 
</Window> 

Ora, questo è solo un esempio per illustrare il mio punto, che è in alternativa è qui per utilizzare il Garbage Collector ...

Se si esegue il programma e si continua a spostare il mouse su Graphics_Canvas, la memoria utilizza build, build e build fino a ottenere uno OutOfMemoryException. Se si aggiunge GC.Collect() dove ho commentato, ciò non accade (sebbene la memoria aumenti di una piccola quantità, per ragioni esterne a me e, si spera, sia collegata al mio problema), e il programma continui a funzionare.

Quindi, perché il GC non avvia e cancella questa memoria e interrompe l'eccezione? Se sto facendo un errore molto fondamentale sarei molto felice che qualcuno me lo indicasse in modo da poter andare oltre.

Ho visto molte volte su questo sito Web e altri consigli dai programmatori che dicono "non usare mai il Garbage Collector". Voglio conformarmi alle migliori pratiche, ma non vedo in situazioni come questa che altro potrei fare.

risposta

9

Non è un errore di GC che il programma non è in grado di gestire molta memoria. Nel programma che fate:

private void Graphics_Canvas_MouseMove(object sender, MouseEventArgs e) 
{ 
    .... 
    //ADD ELEMENTS ON EVERY MOVE ! 
    graphicsCanvas.AddVisual(myVisual); 

    //GC.Collect(); 
} 

Tou aggiungere un elemento ad ogni spostamento del mouse, in modo da aumentare la dimensione raccolta. Cosa ti aspetti che succeda?

Così, in 90% casi, la soluzione per questo tipo di problemi, è riarchitettare codice.

Esempio:

E 'quasi impossibile che avete bisogno su MouseMove aggiungere ogni volta che un nuovo elemento a una collezione di chidren visivo. Potrebbe essere un caso riutilizzare quello già presente.

L'uso esplicito di GC non è suggerito, ma a volte è necessario utilizzarlo. Ma, ripeto, non riesco a credere che nel questo caso sia necessario gestire il programma in quel modo.

+2

Per essere completamente onesti; Trovo strano che il sistema lanci un 'OutOfMemoryException' ** prima ** tentando un 'GC.Collect()'. Solo i miei due centesimi. – Nolonar

+3

Attendi, il codice prima * rimuove * un elemento e poi ne aggiunge un altro. Va bene. –

+1

E la parte 'prima cancella i dati del display corrente'? – RichieHindle

-1

Penso che questo descriva ciò che potrebbe mantenere in vita i tuoi oggetti;

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

In breve; i riferimenti nei gestori di eventi potrebbero ancora esistere.

+1

Se quello era il problema, chiamare 'GC.Collect()' non lo risolveva. – RichieHindle

+0

@IvoTops Sì, ho letto di questo. Comunque il codice postato sopra è l'intero codice per questo programma, e non ho aggiunto eventi, quindi non sono sicuro se questa potrebbe essere la causa. Se la tela aggiunge eventi dietro le quinte, cos'altro posso fare per rimuoverli? – Greg