You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用mingw-w64编译的GTK4拖放时内存泄漏,是代码还是环境问题?

GTK4拖放操作内存泄漏问题排查

在Windows 11系统上使用mingw-w64的g++编译GTK-4(版本4.8.2-1)开发应用时,实现可拖动部件后发现每次拖放操作都会导致应用内存快速增长,重复拖动标签约20次后应用崩溃。以下是复现问题的最小示例代码,预期重复拖动部件不会出现内存泄漏,请问该问题是对API的误解导致,还是GTK或mingw本身的问题?

#include <gtk/gtk.h>

static GdkContentProvider* on_drag_prepare(GtkDragSource *source, double x, double y, GtkWidget* label) {

    GValue* a = new GValue;
    (*a) = G_VALUE_INIT;
    g_value_init(a, G_TYPE_INT);
    g_value_set_int(a, 1);

    // The content is basically a single integer.
    return gdk_content_provider_new_for_value(a);
}

static void activate( GtkApplication *app, gpointer user_data) {

    // window
    GtkWidget* window = gtk_application_window_new (app);   
    gtk_window_set_title (GTK_WINDOW (window), "Drag Memory Leak");
    gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);

    // label to drag
    GtkWidget* label = gtk_label_new("Drag Me");
    gtk_window_set_child(GTK_WINDOW (window), label);

    // setting up drag callback
    GtkDragSource *drag_source = gtk_drag_source_new();
    gtk_drag_source_set_actions(drag_source, GDK_ACTION_MOVE);
    g_signal_connect (drag_source, "prepare", G_CALLBACK (on_drag_prepare), label);
    gtk_widget_add_controller (GTK_WIDGET (label), GTK_EVENT_CONTROLLER (drag_source));

    gtk_window_present (GTK_WINDOW (window));
}

int main( int argc, char **argv) {

    GtkApplication *app;
    int status;

    app = gtk_application_new("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return status;
}

问题原因与解决方案

问题出在对GTK/GDK API的使用错误上,并非GTK或mingw本身的问题。

在on_drag_prepare函数中,你用new手动分配了GValue的堆内存,但**gdk_content_provider_new_for_value()不会接管传入的GValue的所有权**——也就是说这个GValue不会被自动释放。每次拖放操作都会创建一个新的堆分配GValue,且没有任何释放逻辑,导致内存持续泄漏,最终因内存耗尽崩溃。

正确实现方式

推荐使用栈分配的GValue,无需手动管理内存,栈变量会在函数返回时自动销毁:

static GdkContentProvider* on_drag_prepare(GtkDragSource *source, double x, double y, GtkWidget* label) {
    GValue a = G_VALUE_INIT;
    g_value_init(&a, G_TYPE_INT);
    g_value_set_int(&a, 1);
    
    // 创建content provider后,GDK会复制GValue的内容,栈变量销毁不影响
    GdkContentProvider* provider = gdk_content_provider_new_for_value(&a);
    g_value_unset(&a); // 手动清理GValue内部资源
    return provider;
}

如果坚持使用堆分配的GValue,必须在创建provider后手动释放:

static GdkContentProvider* on_drag_prepare(GtkDragSource *source, double x, double y, GtkWidget* label) {
    GValue* a = new GValue;
    (*a) = G_VALUE_INIT;
    g_value_init(a, G_TYPE_INT);
    g_value_set_int(a, 1);
    
    GdkContentProvider* provider = gdk_content_provider_new_for_value(a);
    g_value_unset(a); // 先清理GValue内部资源
    delete a; // 再释放堆内存
    return provider;
}

栈分配的方式更简洁安全,是GTK开发中推荐的用法。

内容的提问来源于stack exchange,提问作者gugsrable

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 08:30:53