【问题标题】:Memory leak on object destruction对象销毁时的内存泄漏
【发布时间】:2014-04-12 08:03:20
【问题描述】:

我有一个玩具项目,它是一个游戏引擎。它使用 SDL2 和 C++11。在下面的代码中,我尝试创建一个在析构函数中自行清理内存的对象。但是出现了问题,并且出现了一些内存泄漏。我做错了什么?

示例是触发泄漏的最小工作代码。我想它是这样工作的: 类Game 的实例在构造时会创建SDLEngineGraphics 的实例(按此顺序),这两个实例也都分配了一些内存。当game 对象被销毁时,它会调用GraphicsSDLEngine 的析构函数(按此顺序)。如果我在这两个析构函数中添加一些打印,它们将按需要的顺序打印。但是valgrind认为SDL_Init()SDL_CreateWindow()分配的内存泄露了。

编辑:这可能是 valgrind 行为。我在非常基本的 SDL 示例中看到了类似的问题和类似的警告:Why does valgrind say basic SDL program is leaking memory?

src/leak-test.cpp:

#include <SDL2/SDL.h>
#include <stdexcept>

class SDLEngine {
public:
    SDLEngine() {
        if (SDL_Init(SDL_INIT_VIDEO) != 0) {
            throw std::runtime_error("SDL_Init"); // line 7
        }
        if (SDL_ShowCursor(SDL_DISABLE) < 0) {
            throw std::runtime_error("SDL_ShowCursor");
        }
    }
    ~SDLEngine() {
        SDL_Quit();
    }
};

class Graphics {
public:
    Graphics() :
        sdlWindow{SDL_CreateWindow(
                    "LeakTest",
                    SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
                    320, 240,
                    0
                    )} // line 27
    {
        if (sdlWindow == nullptr) {
            throw std::runtime_error("SDL_CreateWindow");
        }
    }
    ~Graphics() {
        SDL_DestroyWindow(sdlWindow);
    }
    Graphics(const Graphics&)=delete;
    Graphics& operator=(const Graphics&)=delete;
private:
    SDL_Window *sdlWindow;
};

class Game {
public:
    Game() :
        sdlEngine_(),
        graphics_() // line 46
    {
        SDL_Event event;
        bool running{true};
        while (running) {
            while (SDL_PollEvent(&event)) {
                switch (event.type) {
                case SDL_KEYDOWN:
                    running = false;
                    break;
                default:
                    break;
                }
            }
        }

    }
    ~Game() {}
private:
    const SDLEngine sdlEngine_;
    Graphics graphics_;
};

int main() {
    Game game; // line 70
    return 0;
}

生成文件:

CXX := g++
MKDIR := mkdir -p
CXXFLAGS += `pkg-config --cflags sdl2 SDL2_image`
CXXFLAGS += -Wall -Werror -Wextra -Weffc++ -pedantic -std=c++0x -g
LDFLAGS += `pkg-config --libs sdl2 SDL2_image`
PROG := bin/leak-test
OBJS := $(patsubst src/%.cpp,obj/%.o, $(wildcard src\/*.cpp))
#                         escaped to fool SO parser ^
.PHONY: all clean

all: build

build: $(PROG)

clean:
    rm -rf $(PROG) $(OBJS)

$(PROG): obj/leak-test.o

$(PROG):
    @$(MKDIR) $(dir $@)
    $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $^

obj/%.o : src/%.cpp
    @$(MKDIR) $(dir $@)
    $(CXX) $(CXXFLAGS) -c -MD -o $@ $<

Valgrind 输出:

host:cave-test » valgrind --leak-check=full ./bin/leak-test 
==28815== Memcheck, a memory error detector
==28815== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==28815== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==28815== Command: ./bin/leak-test
==28815== 
==28815== 
==28815== HEAP SUMMARY:
==28815==     in use at exit: 66,235 bytes in 506 blocks
==28815==   total heap usage: 19,844 allocs, 19,338 frees, 44,931,400 bytes allocated
==28815== 
==28815== 20 bytes in 2 blocks are definitely lost in loss record 7 of 101
==28815==    at 0x4C274A0: malloc (vg_replace_malloc.c:291)
==28815==    by 0x5BF8829: strdup (strdup.c:42)
==28815==    by 0x7203666: ??? (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815==    by 0x7204474: _XimSetICValueData (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815==    by 0x71FFA69: _XimLocalCreateIC (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815==    by 0x71E6044: XCreateIC (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815==    by 0x5111CD2: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x51120F7: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x51055FF: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x510540F: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x507048E: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x400D6E: SDLEngine::SDLEngine() (leak-test.cpp:7)
==28815== 
==28815== 20 bytes in 2 blocks are definitely lost in loss record 8 of 101
==28815==    at 0x4C274A0: malloc (vg_replace_malloc.c:291)
==28815==    by 0x5BF8829: strdup (strdup.c:42)
==28815==    by 0x7203666: ??? (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815==    by 0x7204474: _XimSetICValueData (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815==    by 0x71FFA69: _XimLocalCreateIC (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815==    by 0x71E6044: XCreateIC (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0)
==28815==    by 0x5111CD2: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x51120F7: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x51055FF: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x400F11: Graphics::Graphics() (leak-test.cpp:27)
==28815==    by 0x401012: Game::Game() (leak-test.cpp:46)
==28815==    by 0x400D31: main (leak-test.cpp:70)
==28815== 
==28815== 104 bytes in 1 blocks are definitely lost in loss record 60 of 101
==28815==    at 0x4C274A0: malloc (vg_replace_malloc.c:291)
==28815==    by 0xD330A11: ??? (in /usr/lib/mesa-diverted/x86_64-linux-gnu/libGL.so.1.2.0)
==28815==    by 0xD309600: ??? (in /usr/lib/mesa-diverted/x86_64-linux-gnu/libGL.so.1.2.0)
==28815==    by 0xD305E7A: ??? (in /usr/lib/mesa-diverted/x86_64-linux-gnu/libGL.so.1.2.0)
==28815==    by 0xD30660F: glXChooseVisual (in /usr/lib/mesa-diverted/x86_64-linux-gnu/libGL.so.1.2.0)
==28815==    by 0x510ED0E: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x510EF40: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x5103B65: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x51056FB: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x510540F: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x507048E: ??? (in /usr/lib/x86_64-linux-gnu/libSDL2-2.0.so.0.2.0)
==28815==    by 0x400D6E: SDLEngine::SDLEngine() (leak-test.cpp:7)
==28815== 
==28815== LEAK SUMMARY:
==28815==    definitely lost: 144 bytes in 5 blocks
==28815==    indirectly lost: 0 bytes in 0 blocks
==28815==      possibly lost: 0 bytes in 0 blocks
==28815==    still reachable: 66,091 bytes in 501 blocks
==28815==         suppressed: 0 bytes in 0 blocks
==28815== Reachable blocks (those to which a pointer was found) are not shown.
==28815== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==28815== 
==28815== For counts of detected and suppressed errors, rerun with: -v
==28815== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 7 from 3)

【问题讨论】:

  • 请注意,当您在构造函数中抛出时,不会调用析构函数。
  • @Jarod42:但在这种情况下,构造函数会注意释放所有获取的中间内存......因为构造函数调用尚未完成......
  • SDL 库似乎存在一些已知问题,Valgrind 总是显示这样的消息:forums.libsdl.org/…
  • 是的,我认为@MantoshKumar 是对的。我发现了一个触发这种行为的非常小的例子,只有 6 行。
  • @MantoshKumar:我的意思是,实际上,当throw std::runtime_error("SDL_ShowCursor");SDL_INIT 成功时,SDL_QUIT 不会被调用。

标签: c++ memory-management c++11 memory-leaks sdl


【解决方案1】:

事实证明,这个泄漏属于 X11 库中 SDL 下的层。这个错误非常古老,SDL 开发人员知道。请参阅此 bugzilla 线程:https://bugzilla.libsdl.org/show_bug.cgi?id=2086

我现在结束这个问题。

【讨论】:

    猜你喜欢
    • 2011-04-17
    • 2022-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-18
    • 1970-01-01
    • 2011-06-14
    • 1970-01-01
    相关资源
    最近更新 更多