Cairo.Surface正在泄漏......如何使用Monodevelop进行调试?

时间:2014-11-24 13:52:16

标签: memory-leaks mono gtk cairo gtk#

我对Cairo和GTK#(在.NET和Mono上运行)有很多怀疑。我正在为MS Windows和Linux开发GTK#应用程序。我正在使用GTK#2.12而不是.NET,而我正在处理该应用程序。

我创建了一个使用Cairo.ImageSurface和Cairo.Context对象的自定义小部件。据我所知,我正在调用每个ImageSurface对象的Dispose方法以及我在widget代码中创建的每个Context对象。

小部件响应“MouseOver”事件,重新绘制其DrawingArea的某些部分。

(第一个)问题: 几乎每次重绘操作都会增加一点内存量。当已用内存量增加3或4千字节时,Monodevelop tracelog面板显示以下消息:

  

Cairo.Surface漏洞,程序员错过了对Dispose的调用   设置MONO_CAIRO_DEBUG_DISPOSE以跟踪分配跟踪

重绘部分窗口小部件的代码如下:

// SRGB is a custom struct, not from Gdk nor Cairo
void paintSingleBlock(SRGB color, int i)
{
    using (Cairo.Context g = CairoHelper.Create (GdkWindow)) {
        paintSingleBlock (g, color, i);

        // We do this to avoid memory leaks. Cairo does not work well with the GC.
        g.GetTarget().Dispose ();
        g.Dispose ();
    }
}

void paintSingleBlock(Cairo.Context g, SRGB color, int i)
{
    var scale = Math.Pow (10.0, TimeScale);

    g.Save();
    g.Rectangle (x(i), y(i), w(i), h(i));
    g.ClosePath ();
    g.Restore ();

    // We don't directly use stb.Color because in some cases we need more flexibility
    g.SetSourceRGB (color.R, color.G, color.B);
    g.LineWidth = 0;
    g.Fill ();
}

(第二个)问题:好的,Monodevelop告诉我应该将MONO_CAIRO_DEBUG_DISPOSE设置为“跟踪分配痕迹”(为了找到漏洞,我想)...但我不知道如何设置这个环境变量(我在Windows中)。我尝试过使用bash并执行类似的操作:

  

MONO_CAIRO_DEBUG_DISPOSE = 1 ./LightCreator.exe

但是stderr和stdout中没有任何内容......(Monodevelop的应用程序跟踪面板中没有出现的消息)。我也不知道如何获得Monodevelop内部但没有Monodevelop的调试消息。

有没有人有调试GTK#或Cairo#内存泄漏的经验?

提前致谢。

2 个答案:

答案 0 :(得分:1)

我想把我的2c扔到这里,因为我在开罗的表面上遇到类似的泄漏问题。我注意到,如果我创建一个Surface对象,则ReferenceCount属性变为1,如果我将此表面附加到Context,如果变为2而不是3.在处理Context之后,ReferenceCount会返回但是为2。

因此,当我真的想要处理表面时,我使用了一些反射来调用Cairo中的本机方法来减少ReferenceCount。我使用这段代码:

public static void HardDisposeSurface (this Surface surface)
{
    var handle = surface.Handle;
    long refCount = surface.ReferenceCount;
    surface.Dispose ();
    refCount--;
    if (refCount <= 0)
        return;

    var asm = typeof (Surface).Assembly;
    var nativeMethods = asm.GetType ("Cairo.NativeMethods");
    var surfaceDestroy = nativeMethods.GetMethod ("cairo_surface_destroy", BindingFlags.Static | BindingFlags.NonPublic);
    for (long i = refCount; i > 0; i--)
        surfaceDestroy.Invoke (null, new object [] { handle });
}

使用后我仍然有一些泄漏,但它们似乎与开罗的其他部分有关,而与表面无关。

答案 1 :(得分:0)

我发现使用CairoHelper.Create()创建的上下文的引用计数为2。

对dispose的调用会将引用计数减少一个。因此,上下文永远不会被释放,并使其目标保持活力。

本机对象具有手动引用计数,但只要存在引用它的C#实例,Gtk#wrappers就希望保持本机对象的存活。

如果为C#包装器实例创建了本机对象,则不需要增加引用计数,因为包装器实例“拥有”本机对象,并且引用计数具有正确的值1。但是,如果为已存在的本机对象创建了包装器实例,则需要手动增加本机对象的引用计数以使对象保持活动状态。

这是在创建包装器实例时由bool参数决定的。

查看CairoHelper.Create()的代码将显示类似这样的内容

public static Cairo.Context Create(Gdk.Window window) {
            IntPtr raw_ret = gdk_cairo_create(window == null ? IntPtr.Zero : window.Handle);
            Cairo.Context ret = new Cairo.Context (raw_ret, false);
            return ret;
        }

即使原始上下文刚创建,“拥有”也将为false,C#上下文将增加引用计数。

目前没有固定版本,只能通过修复源代码并自行构建Gtk#来纠正。

CairoHelper是一个自动生成的文件,要将参数更改为true,此属性必须包含在gdk / Gdk.metadata中。

<attr path="/api/namespace/class[@cname='GdkCairo_']/method[@name='Create']/return-type" name="owned">true</attr>

可以在这里找到构建Gtk#的所有内容。 https://github.com/mono/gtk-sharp

相关问题