使用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
相关产品推荐
相关产品推荐

