首页 > 解决方案 > hboehm 垃圾收集器未初始化值错误和泄漏

问题描述

我正在试验hboehm 垃圾收集器,现在只是尝试运行他们的简单示例。问题是,当我用 Valgrind 检查结果时,我得到很多“使用未初始化值”的错误,而且具有讽刺意味的是,内存泄漏。

重现问题的最小示例的完整项目。

cmake_minimum_required(VERSION 3.10)

# This "outer" CMake file works as a dependency fetcher, and it consists almost exclusively of ExternalProject commands.
# The "inner" CMake file under project/ is a more traditional CMake file. It uses the find_* commands to locate
# dependencies without any knowledge or assumptions about where those dependencies live or how they got there.

include(ExternalProject)

ExternalProject_Add(
    libatomic_ops
    URL http://www.hboehm.info/gc/gc_source/libatomic_ops-7.6.2.tar.gz
    CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND ""
)
ExternalProject_Get_Property(libatomic_ops SOURCE_DIR)
set(libatomic_ops_SOURCE_DIR "${SOURCE_DIR}")

ExternalProject_Add(
    hboehm_gc
    URL http://www.hboehm.info/gc/gc_source/gc-7.6.4.tar.gz
    DEPENDS libatomic_ops
    BUILD_IN_SOURCE TRUE
    # hboehm_gc requires libatomic_ops be placed in a libatomic_ops subdirectory
    PATCH_COMMAND "${CMAKE_COMMAND}" -E copy_directory "${libatomic_ops_SOURCE_DIR}" "<SOURCE_DIR>/libatomic_ops"
    CONFIGURE_COMMAND ./configure "--prefix=<INSTALL_DIR>"
    BUILD_COMMAND make
    INSTALL_COMMAND make install
)
ExternalProject_Get_Property(hboehm_gc INSTALL_DIR)
set(hboehm_gc_INSTALL_DIR "${INSTALL_DIR}")

# Now that we have our dependencies on disk to be found, it's safe to configure (run cmake on) the real project
ExternalProject_Add(
    main DEPENDS hboehm_gc
    DOWNLOAD_COMMAND ""
    SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/project"
    BUILD_ALWAYS TRUE
    CMAKE_ARGS "-DCMAKE_PREFIX_PATH=${hboehm_gc_INSTALL_DIR}"
    INSTALL_COMMAND ""
    TEST_COMMAND "${CMAKE_CTEST_COMMAND}" --verbose
)

cmake_minimum_required(VERSION 3.10)

find_path(HBOEHM_GC_INSTALL_DIR include/gc.h)
find_program(VALGRIND_COMMAND valgrind)

add_executable(main src/main.cpp)
target_compile_features(main PRIVATE cxx_std_14)
target_link_libraries(main PRIVATE "${HBOEHM_GC_INSTALL_DIR}/lib/libgc.a" pthread)
target_include_directories(main PRIVATE "${HBOEHM_GC_INSTALL_DIR}/include")

enable_testing()
add_test(NAME valgrind COMMAND "${VALGRIND_COMMAND}" "$<TARGET_FILE:main>")
set_tests_properties(
    valgrind PROPERTIES
    PASS_REGULAR_EXPRESSION "ERROR SUMMARY: 0 errors from 0 contexts"
)

#include <cassert>
#include <iostream>
#include <gc.h>

int main() {
  int i;

  GC_INIT();    /* Optional on Linux/X86; see below.  */
  for (i = 0; i < 10000000; ++i)
   {
     int **p = (int **) GC_MALLOC(sizeof(int *));
     int *q = (int *) GC_MALLOC_ATOMIC(sizeof(int));
     assert(*p == 0);
     *p = (int *) GC_REALLOC(q, 2 * sizeof(int));
     if (i % 100000 == 0)
       std::cout << "Heap size = " << GC_get_heap_size() << "\n";
   }
  return 0;
}

只需要:

mkdir build
cd build
cmake ..
make

它将下载 hboehm_gc 和依赖项,构建项目并运行 valgrind。我希望我只是以某种方式滥用图书馆,但我不确定我做错了什么。任何帮助表示赞赏。

标签: c++garbage-collection

解决方案


您应该期望 Boehm 的收集器有一些内存泄漏(因为它是一个保守的GC)。由于 Boehm GC 是一个保守的 GC,它不(也不能)提供强有力的保证。但是你希望它不会有太多的泄漏或内存浪费(一些论文提到了 20% 的泄漏率,这是 Boehm 在 Linux/x86-64 上的 GC 的典型表现)。Boehm 的 GC 有一个关于保守垃圾收集的优点和缺点的页面,你绝对应该阅读。还有对它的详细描述,最后它是免费软件,所以你可以(也许应该)研究它的源代码。

Boehm 和 valgrind 都使用类似的技术,因此它们不能很好地配合使用。显然 valgrind 会在使用 Boehm GC 的任何代码中检测到大量内存泄漏。在链接 Boehm 的 GC 的代码上使用 valgrind 是没有用的。您可以明确清除使用GC_MALLOC.

如果您需要精确的 GC(特别是如果您需要更多关于 GC 的保证),请选择其他东西,或者编写自己的代码(天真的精确标记和扫描 stop-the-world GC 很容易编码,至少在单线程中)程序;无聊的部分是维护 GC 根并提供对包含指针的本地“变量”的访问。您将struct在每个调用帧中将它们放在一些中,并将这些struct-s 链接在一起)。也许看看Ravenbrook 的 MPS,或者我的旧的、无人维护的、有缺陷的Qish(也许它可以激发你的灵感)。还要查看Ocaml GC 以及如何将C 与 Ocaml 接口

另请阅读GC 手册

顺便说一句,您的问题令人惊讶:valgrind(它的memcheck工具)用于寻找丢失的 -s,而 Boehm GC 的全部目的是通过提供不需要任何类型的释放操作的(替换)free来呈现free“无用” (所以在执行-s的程序上使用 valgrind 是没有意义的)。GC_MALLOCmallocGC_MALLOC


推荐阅读