HBOEHM 垃圾回收器未初始化的值错误和泄漏

hboehm garbage collector uninitialized value errors and leaks

本文关键字:错误 泄漏 初始化 HBOEHM      更新时间:2023-10-16

我正在试验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。我希望我只是以某种方式滥用了图书馆,但我不确定我做错了什么。任何帮助,不胜感激。

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

Boehm和valgrind都使用类似的技术,所以他们不能很好地合作。显然,valgrind 会检测到使用 Boehm GC 的任何代码中的大量内存泄漏。在链接Boehm的GC的代码上使用valgrind是没有用的。您可以显式清除使用GC_MALLOC获得的每个内存区域。

如果你需要一个精确的GC(特别是如果你需要更多关于GC的保证(,选择其他东西,或者编写你自己的代码(一个天真的精确标记和扫描停止世界GC很容易编码,至少在单线程程序中是这样;无聊的部分是维护GC根并允许访问包含指针的本地"变量"。您需要将它们放在每个呼叫帧中的一些struct中,并将这些struct-s 链接在一起(。也许看看Ravenbrook的MPS,或者我的旧,无人维护和越野车Qish(也许它可以激发你的灵感(。还要研究 Ocaml GC 以及如何将 C 与 Ocaml 接口。

另请阅读GC手册。

顺便说一句,你的问题令人惊讶:valgrind(它的memcheck工具(用于搜寻丢失的free-s,Boehm GC的全部意义是通过提供一个不需要任何释放操作的GC_MALLOC(替换malloc(来使free"无用"(所以在执行GC_MALLOC-s的程序上使用valgrind是没有意义的(。