8

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

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

を作成する XAMLWindowは次のようになります。

<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_CanvasOutOfMemoryException. GC.Collect()私がコメントアウトした場所に追加すると、これは発生しません (ただし、メモリは少し増加しますが、理由は私を超えており、うまくいけば私の問題にリンクされています)。プログラムは引き続き機能します。

では、なぜGCこのメモリを起動してクリアし、例外の発生を停止しないのでしょうか? 私が非常に根本的な間違いを犯している場合は、誰かがそれを指摘してくれれば、私はこれを乗り越えることができます.

この Web サイトや他のプログラマーからの「ガベージ コレクターは絶対に使用しないでください」というアドバイスを何度も見てきました。ベストプラクティスに準拠したいのですが、このような状況で他に何ができるかわかりません。

4

2 に答える 2

10

GCプログラムが多くのメモリを管理できないのは、問題ではありません。プログラムでは、次のことを行います。

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

    //GC.Collect();
}

すべてのマウスの動きに要素を追加するには、コレクションのサイズを増やします。何が起こると思いますか?

したがって、90%のケースで、この種の問題の解決策はコードを再構築することです。

例:

ビジュアルの子供コレクションに新しい要素を追加するたびに、MouseMove が必要になることはほとんどありません。すでにあるものを再利用するケースかもしれません。

の明示的な使用は推奨されGCていませんが、使用する必要がある場合があります。しかし、繰り返しますが、この場合、そのような方法でプログラムを管理する必要があるとは信じがたいです。

于 2013-03-25T09:30:33.920 に答える
-1

これは、オブジェクトを存続させている可能性があることを説明していると思います。

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

要するに; イベント ハンドラー内の参照がまだ存在している可能性があります。

于 2013-03-25T09:51:41.433 に答える