2014-11-24 29 views
2

我对开罗和GTK#有很多疑问(运行在.NET和Mono上)。我正在为MS Windows和Linux开发一个GTK#应用程序。我正在使用应用程序时,我正在使用GTK#2.12。Cairo.Surface正在泄漏...如何使用Monodevelop进行调试?

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

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

(第一个)问题: 几乎每个重绘操作都会增加一点点使用的内存量。时使用的存储器的量增加3个或4千字节的MonoDevelop中追踪日志面板显示我以下消息:

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

但没有出现在标准错误,也没有标准输出...(既不消息出现在MonoDevelop中的应用跟踪面板)。我也不知道如何获取Monodevelop内部但没有Monodevelop的调试信息。

有人有调试GTK#或开罗#内存泄漏的经验吗?

在此先感谢。

+0

您是否得到了一些改进? – 2014-12-13 15:28:14

+0

我已经成功地清除了我的项目中的所有泄漏。一个来源是'FontOptions'用法。只要您从上下文修改字体选项,它就会开始泄漏...我必须确认其他潜在来源... – 2014-12-13 19:49:00

+0

我还销毁了绘制调用之间的所有表面,我将字节数组中的位图缓存并从中重新创建表面周期(必须确认这是绝对必要的) – 2014-12-13 19:53:44

回答

1

只想把我的2c扔在这里,因为我在开罗用表面处理类似的泄漏问题。我注意到,如果我创建Surface对象,则ReferenceCount属性将变为1,如果我将此曲面附加到上下文(如果变为2但不是3)。在配置上下文后,ReferenceCount会返回,但会返回到2.

因此,我使用一些反射来调用开罗的本地方法来减少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 }); 
} 

使用它,我仍然有一些泄漏之后,但他们似乎涉及到开罗的其他部分并没有与表面。

+0

我现在没有访问该代码测试你的建议,但这个回应无论如何都值得奖励:)。 – castarco 2016-10-20 22:15:09

0

我发现使用CairoHelper.Create()创建的上下文将具有两个引用计数。

处理调用将参考计数减1。因此,环境永远不会被释放,并保持目标的活力。

本地对象具有手动引用计数,但只要有C#实例引用它,Gtk#wrappers就想保持本机对象的活动状态。

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

这是在创建包装器实例时由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; 
     } 

即使天然情况下刚刚创建“拥有”将是错误的和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