2014-11-24 28 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