2013-03-25 95 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