【问题标题】:What is the alternative to the GarbageCollector?GarbageCollector 的替代品是什么?
【发布时间】:2013-03-25 09:24:37
【问题描述】:

我有以下代码,运行时会生成OutOfMemoryException

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

而用于创建Window 的 XAML 是这样的:

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

现在,这只是说明我的观点的一个例子,我不明白这里使用垃圾收集器的替代方案是什么......

如果您运行该程序,并继续将鼠标移到Graphics_Canvas 上,内存使用量会不断增加,不断增加,直到您获得OutOfMemoryException。如果您在我已注释掉的地方添加GC.Collect(),则不会发生这种情况(尽管内存确实增加了少量,但出于我以外的原因,并希望与我的问题有关),并且程序继续运行.

那么,为什么GC 不启动并清除此内存并停止发生异常?如果我犯了一些非常根本的错误,我会很高兴有人向我指出,这样我就可以超越这一点。

我在这个网站和其他程序员的建议中多次看到“永远不要使用垃圾收集器”。我想遵循最佳实践,但在这种情况下我看不到我还能做什么。

【问题讨论】:

    标签: c# .net wpf garbage-collection


    【解决方案1】:

    程序无法管理太多内存不是GC 的错。 在你的程序中你这样做:

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

    要为每个鼠标移动添加一个元素,因此增加集合大小。你期望会发生什么?

    因此,在 90% 的情况下,此类问题的解决方案是重新架构您的代码。

    例子:

    您几乎不可能每次都在 MouseMove 上将新元素添加到儿童视觉集合中。可能是重复使用已经存在的情况。

    建议明确使用GC,但有时我们需要使用它。但是,我再说一遍,我几乎不敢相信在这种情况你需要以这种方式管理程序。

    【讨论】:

    • 老实说;我觉得很奇怪,系统会在尝试GC.Collect() 之前抛出一个OutOfMemoryException。只是我的两分钱。
    • 等等,代码是先删除一个元素,然后再添加另一个。没关系。
    • @Tigran 你说得对,我不需要在 MouseMove 上添加 Visual,但这是一个快速生成错误的示例,仅此而已。此外,我在添加新视觉之前删除了一个视觉,如果我在每个 MouseMove 上输出画布上的视觉数量,它会保持在 1 不变。
    • @KonradRudolph:在这个集合中删除和添加元素并不能证明什么。这并不意味着被移除的元素会在之后被 GC 移除,也不意味着即使它适合移除,由于某种原因,它不会因为经过太久的世代而留在内存中。
    • @Tigran 这不是我的意思。我的观点是,这是 GC通常能够处理的可接受的使用模式。您的回答强烈暗示代码只是将对象添加到集合中而不删除它们,并且 OP 的代码导致内存泄漏。事实并非如此。
    【解决方案2】:

    我认为这描述了可能使您的对象保持活力的原因;

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

    简而言之;事件处理程序中的引用可能仍然存在。

    【讨论】:

    • 如果这是问题所在,致电GC.Collect() 将无法解决问题。
    • @IvoTops 是的,我已经读过这个。但是上面发布的代码是该程序的全部代码,我没有添加任何事件,所以我不确定这是否是原因。如果 Canvas 在幕后自行添加事件,我还能做些什么来删除它们?
    猜你喜欢
    • 2019-02-22
    • 2011-05-07
    • 2012-04-24
    • 2019-12-03
    • 2010-09-17
    • 2012-03-27
    • 2012-09-30
    • 2010-10-20
    • 2014-03-12
    相关资源
    最近更新 更多