2013-03-25 150 views
7

我有下面的代碼,這將產生一個OutOfMemoryException運行時:什麼是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; 
    } 
} 

Graphics_Canvas定義如下:

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);    
    } 
} 

而XAML創建Window是這樣的:

<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> 

現在,這只是一個例子來說明我的觀點,那就是我不明白wh在另一種方法是在這裏使用的垃圾收集器...

如果你運行該程序,並保持在Graphics_Canvas移動鼠標,內存使用建立和建立,並建立,直到你得到一個OutOfMemoryException。如果你在GC.Collect()的地方添加了我已經評論過的內容,這種情況不會發生(儘管由於我之外的原因,內存增加了一小部分,並希望與我的問題聯繫起來),並且程序繼續運行。

那麼,爲什麼GC不啓動並清除此內存並停止發生異常?如果我正在做一些非常根本性的錯誤,我會很高興有人向我指出這一點,以便我可以超越這一點。

我在這個網站上看過很多次,還有一些程序員的建議是「永遠不要使用垃圾收集器」。我想遵守最佳做法,但在這種情況下我沒有看到我可以做什麼。

回答

9

這不是GC的錯誤,即程序無法管理太多內存。 在你的程序你這樣做:

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

    //GC.Collect(); 
} 

頭的元素添加到每個鼠標移動,因此增加了集合的大小。你會期望發生什麼?

所以,在90%箱子,對於此類問題的解決方案,是重新構建你的代碼。

例子:

這幾乎是不可能的,你需要上的MouseMove新元素添加每次的視覺一個chidren集合。可能是重用已存在的一個案例。

明確使用GC不是建議,但有時我們需要使用它。但是,我再說一遍,我很難相信在這種情況下您需要以這種方式管理程序。

+2

完全誠實;我覺得奇怪,系統會在嘗試'GC.Collect()'之前拋出'OutOfMemoryException' **。只是我的兩分錢。 – Nolonar 2013-03-25 09:32:58

+3

等待,代碼首先*刪除*一個元素,然後添加另一個元素。沒關係。 – 2013-03-25 09:34:55

+1

「首先清除當前顯示數據」部分呢? – RichieHindle 2013-03-25 09:42:46

-1

我認爲這描述了什麼可能會讓你的對象活着;

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

總之;事件處理程序中的引用可能仍然存在。

+1

如果這是問題,調用'GC.Collect()'不會解決它。 – RichieHindle 2013-03-25 09:53:38

+0

@IvoTops是的,我已閱讀關於此。然而,上面貼出的代碼是這個程序的整個代碼,並且我沒有添加任何事件,所以我不確定這是否可能是原因。如果畫布在幕後添加事件,我還能做些什麼來移除它們? – Greg 2013-03-25 09:56:05

相關問題