我对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#内存泄漏的经验?
提前致谢。
答案 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