gtkmm/C++首个Hello World示例存在内存泄漏问题。

7
我正在尝试学习gtkmm,并决定尝试gtkmm 2.4,因为在Debian上似乎很难使3.0正常工作。无论如何,我正在尝试的示例在这里:http://developer.gnome.org/gtkmm-tutorial/2.24/sec-helloworld.html.en。它可以编译成功并且运行良好,但是当我关闭它时,valgrind报告了很多泄漏,大致如下(点击按钮一次后):
==4254== Memcheck, a memory error detector
==4254== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==4254== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==4254== Command: ./bin/jmb
==4254== 
Hello World
==4254== 
==4254== HEAP SUMMARY:
==4254==     in use at exit: 942,940 bytes in 7,968 blocks
==4254==   total heap usage: 14,191 allocs, 6,223 frees, 3,272,961 bytes allocated
==4254== 
==4254== LEAK SUMMARY:
==4254==    definitely lost: 2,620 bytes in 6 blocks
==4254==    indirectly lost: 5,936 bytes in 187 blocks
==4254==      possibly lost: 358,625 bytes in 1,775 blocks
==4254==    still reachable: 575,759 bytes in 6,000 blocks
==4254==         suppressed: 0 bytes in 0 blocks
==4254== Rerun with --leak-check=full to see details of leaked memory
==4254== 
==4254== For counts of detected and suppressed errors, rerun with: -v
==4254== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 9 from 9)

如果我使用C-c停止程序或点击关闭窗口按钮(在这种情况下,由于窗口管理器,我必须使用Shift-Meta-C来关闭窗口),就会发生这种情况。这是MySQL连接器不允许您删除最后一个指针的预期行为吗?在这种情况下,似乎有很多内存无法被“允许”删除?还是我错过了什么非常简单的东西?
以下是我的代码:(将HelloWorld更改为Test) main.cpp:
#include "gui/Test.hpp"
#include <gtkmm/main.h>
int main(int argc, char **argv)
{
  Gtk::Main kit(argc, argv);
  Test t;
  Gtk::Main::run(t);
  return 0;
}

Test.hpp:

#pragma once

#include <gtkmm/button.h>
#include <gtkmm/window.h>

class Test
  : public Gtk::Window
{
public:
  Test();
  virtual ~Test();

protected:
  //Signal handlers:
  void on_button_clicked();

  //Member widgets:
  Gtk::Button m_button;
};

Test.cpp:

#include "Test.hpp"
#include <iostream>

Test::Test()
  : m_button("Hello World")   // creates a new button with label "Hello World".
{
  // Sets the border width of the window.
  set_border_width(10);

  // When the button receives the "clicked" signal, it will call the
  // on_button_clicked() method defined below.
  m_button.signal_clicked().connect(sigc::mem_fun(*this,
              &Test::on_button_clicked));

  // This packs the button into the Window (a container).
  add(m_button);

  // The final step is to display this newly created widget...
  m_button.show();
}

Test::~Test()
{

}

void Test::on_button_clicked()
{
  std::cout << "Hello World" << std::endl;
}

提前感谢您!


这不是针对你的gtk问题的正确答案,但通常在调查内存泄漏时,使用valgrind --leak-check=full --leak-resolution=high --track-origins=yes可以获得更多细节。 - Jonathan Wakely
这给了我太多信息,我无法处理它。我应该添加一个输出链接吗? - lfxgroove
1
我建议你仔细检查并理解它,这将是有用的、有价值的练习,将帮助你在未来。其他人可以为你解释,但如果他们能做到,那么你也可以。如果需要的话,你可以删除“--leak-resolution=high”以减少输出。 - Jonathan Wakely
看起来错误主要来自于libpango、libgobject和libcairo。可能是我的窗口处理程序引起的吗? - lfxgroove
不是窗口处理程序的问题,我刚刚尝试了gnome和awesome,两者都有内存泄漏。 - lfxgroove
3个回答

3
考虑到您没有动态内存分配,因此泄漏不是来自您的代码。由于您在堆栈上定义了Test t变量而不是动态定义,所以当它超出范围时将被删除。这将在main()函数完成时发生,实际上是在整个程序完成之前。Test类也没有任何直接的动态内存分配。通过提到直接,我的意思是直接在该类中,但不在其包含的属性(gtk等)中。
为了证明您的Test实例确实被删除,您可以在析构函数中放置printf。您应该在应用程序退出时看到输出。
即使您动态定义/创建了Test实例,您也应该养成始终删除所有创建的习惯。在这种情况下,它只是内存,但它可能是更有价值的资源,如DB连接、文件系统资源或需要在退出时执行的某些其他逻辑。
Valgrind可能会给您提供实际内存泄漏发生的堆栈跟踪,我相信您会看到它们在gtk代码中。如果是这样,我会考虑提出一个错误。我不会太担心still reachable内存(它实际上不是泄漏),而是要查看definitely lostindirectly lost

请问您能否解释一下为什么“still reachable”不是真正的泄漏,或者给我一些资源来说明这一点?此外,我已经使用--show-origin=yes运行了Valgrind,并且它显示大部分是gtk,但也有cairo和pango,我是否应该在那里提交错误报告?提前感谢! - lfxgroove
@Anton,内存泄漏是指您不再访问的内存,这意味着您曾经有一个指向该内存的指针,然后在没有释放该内存的情况下,将不同的值分配给指针,从而“丢失”其地址。仍然可达的内存是仍然被指针引用的内存,并且程序终止,因此内存并未“丢失”。至于错误,我会说是的,但可能是gtk没有正确管理这些库,因此它们可能会拒绝它。当您提交错误时,请显示Valgrind堆栈跟踪,他们会感激的 :) - Brady
@Anton,我应该提到,“仍然可达”不像泄漏那么严重,但最好还是要解决。 - Brady

2
假设Gtk--没有内存泄漏,你发布的输出结果可以符合这个假设。
程序退出时仍有一些内存可访问并不等同于内存泄漏。
以下程序是否存在内存泄漏?
int main() {
     int* a = new int[10];
     return 0;
}

看起来是这样,但完整的答案应该是:取决于情况!

如果我们没有使用MS/DOS并且在程序结束之前需要“a”,那么这很难被定义为泄漏...我们可以说程序员将清理的责任留给了底层操作系统。

此外,Valgrind是一个非常好用和有用的工具,但可能会报告错误的正面结果。


那么你的意思是他们不能/不会清理最后一块内存并将其留给底层操作系统吗?这方面有什么文档资料吗?另外,这种情况通常吗? - lfxgroove
我只是做了一般性的评论,我不能代表gtkmm开发人员发言。我认为询问他们打算做什么的正确地方是他们的邮件列表。 - baol

1

将G_SLICE环境变量设置为重新配置GSlice内存分配器。

G_SLICE=always-malloc ./your_application

请查看此帖子以获取详细信息。

我尝试了G_SLICE=always-malloc和G_DEBUG=gc-friendly[,resident-modules],但并没有什么帮助。我下载了提到的抑制文件,它似乎抑制了一些错误。 - lfxgroove

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接