Libelektra: KeySet关闭内存泄漏

创建于 2019-11-04  ·  32评论  ·  资料来源: ElektraInitiative/libelektra

在对新的elektrad进行基准测试时,我遇到了内存泄漏症状。
创建新的句柄并调用kdbGet()后,内存使用量将增加,但在ksClose()kdbClose()之后,内存使用量将不会减少。

我的第一个怀疑是mmap缓存-这可能还会导致内存泄漏,因为从未释放过mmap的内存-但是通过使用-DPLUGINS="ALL;-cache"禁用缓存并不能解决我的问题。

重现问题的步骤

转到> = 1.13

我在go-elektra回购中创建了两个测试。 两者都创建具有100000个键的测试键集。

  1. TestKeySetMemory在循环中创建句柄和kdbGets键集,等待1秒后-立即重新关闭键集+句柄,然后重新开始。
  2. TestKeySetMemoryWithDelayedClose还会创建句柄并用kdbGets填充键集-但会延迟关闭句柄和键集,直到所有20个键集都加载完毕。 这模仿了elektrad Web服务器的行为。

两项测试在完成后都将等待20秒,以允许测试人员通过htop或类似工具查看测试的内存消耗。

立即关闭手柄和按键组的第一个测试在整个测试过程中保留了相同的内存占用量。

仅在“加载”每个键集之后才开始关闭句柄和键集的第二项测试永远不会释放任何内存。 即使强行执行垃圾收集并等待20秒。

目前,我不知道为什么这些测试的行为会有所不同。

您可以通过克隆go-elektra库并在./kdb子目录中运行以下两个命令来运行测试:

PKG_CONFIG_PATH=<PATH TO elektra.pc FILE> go test  -run "^(TestKeySetMemory)\$"

PKG_CONFIG_PATH=<PATH TO elektra.pc FILE> go test  -run "^(TestKeySetMemoryWithDelayedClose)\$"

预期结果

kdbCloseksClose之后释放内存

实际结果

内存无法释放

系统信息

  • Elektra版本:大师
  • 操作系统:Arch Linux
  • 最新的go-elektra版本
bug urgent work in progress

所有32条评论

在我看来,这样的事情在我们的valgrind和ASAN测试中似乎不太可能出现。 我一直在C中运行类似的基准测试,但无法观察到相同的结果。 即使使用mmap,我也无法观察到任何类似的结果。 我不排除它。

您确定绑定没有泄漏到某个地方吗?

是的,我敢肯定-可以在运行时检查go应用程序的内存使用情况,这些统计信息不包含C代码分配,并且它们与应用程序/测试的总内存使用率相差甚远,这意味着大部分内存在C中分配。

我的怀疑是键没有释放,因为ref计数器是!= 0即使它应该是0

这意味着绑定以触发此内存泄漏的方式调用C API。 不幸的是我不知道去,所以我不知道为什么会这样。

您能否提取一个触发泄漏的小型C示例? 我在benchmarks/kdb.c执行了非常类似的操作,并且那里没有泄漏,因此它需要完全执行基准测试来触发泄漏。

感谢您报告此问题!

我在这里完全同意@mpranj ,我们需要将问题减少到最小程度,因为涉及多种语言,所以很难找到任何东西。

您能否编写一个C程序来重现go绑定发出的调用序列? 一旦有了这些,就可以最小化一个测试用例的C程序。

顺便提一句。 试图运行上面的命令,我很早就失败了:#3159

直到明天,我都会尝试用C语言重现它。

在这里成功复制了此问题。 运行benchmarks/memoryleak.c基准测试,并观察获取键集时内存增加-释放时减少内存。

不要忘记在数据库中添加大量密钥。.我用100k个密钥对其进行了测试。

感谢您创建示例! 我会看看。

乍一看,确实有一个直接泄漏,您需要释放parentKey:
https://github.com/raphi011/libelektra/blob/afcbcf5c8138be8de6ba9b1a9e559bc673ff887f/benchmarks/memoryleak.c#L22

这种小的泄漏可能不是您进行观察的原因。

附带说明:使用mmap高速缓存时,与没有高速缓存时相比,我注意到甚至更少的内存消耗。 也许我需要将其添加到我的论文中:微笑:。 (缓存:〜200M,无缓存:〜600M)

似乎我们正在通过dlopen() / dlclose() “泄漏”内存。 通过使用kdb可以观察到以下情况,但是不使用kdb-static (仅摘录)可以观察到以下情况:

valgrind --leak-check=full --show-leak-kinds=all ./bin/kdb ls user
[...]
==48451== 1,192 bytes in 1 blocks are still reachable in loss record 6 of 6
==48451==    at 0x483AB1A: calloc (vg_replace_malloc.c:762)
==48451==    by 0x400BB11: _dl_new_object (in /usr/lib64/ld-2.30.so)
==48451==    by 0x400642F: _dl_map_object_from_fd (in /usr/lib64/ld-2.30.so)
==48451==    by 0x4009315: _dl_map_object (in /usr/lib64/ld-2.30.so)
==48451==    by 0x400DB24: openaux (in /usr/lib64/ld-2.30.so)
==48451==    by 0x4DFE8C8: _dl_catch_exception (in /usr/lib64/libc-2.30.so)
==48451==    by 0x400DF6A: _dl_map_object_deps (in /usr/lib64/ld-2.30.so)
==48451==    by 0x4013AC3: dl_open_worker (in /usr/lib64/ld-2.30.so)
==48451==    by 0x4DFE8C8: _dl_catch_exception (in /usr/lib64/libc-2.30.so)
==48451==    by 0x401363D: _dl_open (in /usr/lib64/ld-2.30.so)
==48451==    by 0x496139B: dlopen_doit (in /usr/lib64/libdl-2.30.so)
==48451==    by 0x4DFE8C8: _dl_catch_exception (in /usr/lib64/libc-2.30.so)
[...]

您的示例打开了许多句柄,并增加了更多的句柄,因此它看起来像这样:

==48735== 72,704 bytes in 1 blocks are still reachable in loss record 8 of 8
==48735==    at 0x483880B: malloc (vg_replace_malloc.c:309)
==48735==    by 0x4F860A9: ??? (in /usr/lib64/libstdc++.so.6.0.27)
==48735==    by 0x400FD59: call_init.part.0 (in /usr/lib64/ld-2.30.so)
==48735==    by 0x400FE60: _dl_init (in /usr/lib64/ld-2.30.so)
==48735==    by 0x4013DBD: dl_open_worker (in /usr/lib64/ld-2.30.so)
==48735==    by 0x4A088C8: _dl_catch_exception (in /usr/lib64/libc-2.30.so)
==48735==    by 0x401363D: _dl_open (in /usr/lib64/ld-2.30.so)
==48735==    by 0x48C739B: dlopen_doit (in /usr/lib64/libdl-2.30.so)
==48735==    by 0x4A088C8: _dl_catch_exception (in /usr/lib64/libc-2.30.so)
==48735==    by 0x4A08962: _dl_catch_error (in /usr/lib64/libc-2.30.so)
==48735==    by 0x48C7B08: _dlerror_run (in /usr/lib64/libdl-2.30.so)
==48735==    by 0x48C7429: dlopen@@GLIBC_2.2.5 (in /usr/lib64/libdl-2.30.so)

在该示例中,您打开了许多KDB句柄,这导致这种效果加起来导致大量内存消耗。 如果只打开一个手柄,则应该少一些。 @ raphi011我不知道您是否可以使用kdb-static作为基准。 将基准链接到elektra-static库时,我再也观察不到相同的结果:

==54836== HEAP SUMMARY:
==54836==     in use at exit: 0 bytes in 0 blocks
==54836==   total heap usage: 6,456,631 allocs, 6,456,631 frees, 272,753,180 bytes allocated
==54836== 
==54836== All heap blocks were freed -- no leaks are possible
==54836== 
==54836== For lists of detected and suppressed errors, rerun with: -s
==54836== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

我目前不确定dlopen()可以解决的问题。

这是新的elektrad服务器的简化模拟。 由于我们希望支持多个用户同时配置系统,因此我们需要支持多个句柄(冲突处理等)。

我仍然认为这不是问题的根源。 问题在于键/键集没有被释放。 如果将父键更改为"system" ,而键的数量要少得多,则会发现内存消耗要低得多。

@ markus2330您怎么看?

编辑:忘记这一点。

我不认为我们在那里泄漏了内存。
https://gist.github.com/mpranj/bbdf00af308ed3f5b3f0f35bc832756f~~

我可以用上面的要旨观察到与HTOP和内存使用情况相同的内容。

因此,您告诉我,elektra的内存消耗从未减少是绝对正常的吗?

因此,您告诉我,elektra的内存消耗从未减少是绝对正常的吗?

我不是那个意思我的意思是不管elektra我都观察到相同的结果。 我只是注意到我的观察仅在使用valgrind运行时才是正确的,因此也许valgrind直到其他结尾才释放()直到结束。

https://stackoverflow.com/questions/40917024/memory-leak-after-using-calloc-and-free

好像是因为要在释放指针之前先更改指针

好像是因为要在释放指针之前先更改指针

我没有这样做,并且valgrind在要点中没有显示代码泄漏。 但是忘了这个例子,它是无关紧要的,因为它只在valgrind内部运行时不会立即释放,否则内存会立即被free()d释放。

更相关的是:我观察到您只是通过使用benchmark_createkeys根本不使用KDB来报告。 那里的资源也不是立即被free()d的,但是valgrind绝对显示0泄漏。 我很困惑。

@ markus2330您怎么看?

单独使用键集绝对绝对不会泄漏。 (使用KDB,我们无法完全控制所有内容,因为插件可能会加载其他可能泄漏的库。)

@ raphi011您可以创建PR还是将我

https://github.com/raphi011/libelektra/tree/memoryleak ,您在这里。 我通过打印ksDel由于键引用> 0未能释放多少键而扩展了前面的示例。

如果在没有valgrind的

我无法编译此存储库,但出现错误:

CMake Error: Error processing file: /home/markus/Projekte/Elektra/repos/libelektra/scripts/cmake/ElektraManpage.cmake
make[2]: *** [src/bindings/intercept/env/CMakeFiles/man-kdb-elektrify-getenv.dir/build.make:61: ../doc/man/man1/kdb-elektrify-getenv.1] Fehler 1
make[1]: *** [CMakeFiles/Makefile2:17236: src/bindings/intercept/env/CMakeFiles/man-kdb-elektrify-getenv.dir/all] Fehler 2

您可以改用主要主人吗?

并且请进行PR,然后,更容易看到更改。

如果在没有valgrind的情况下运行基准测试,则可以看到未释放键。

您可以在此处复制运行的输出吗?

我观察到您通过简单地使用Benchmark_createkeys来报告的内容,而根本不使用KDB。 那里的资源也不是立即被free()d的,但是valgrind绝对显示0泄漏。 我很困惑。

我认为我们应该首先遵循此跟踪,因为如果我们只有KeySet,则更容易理解。

也许我们对valgrind添加了错误的抑制?

附带说明:使用mmap高速缓存时,与没有高速缓存时相比,我注意到甚至更少的内存消耗。 也许我需要将此添加到我的论文微笑中。 (缓存:〜200M,无缓存:〜600M)

这些肯定是个好消息。

在这里您可以前往: https :

也许我们对valgrind添加了错误的抑制?

Valgrind报告说,我进行测试时没有抑制,但是您的行驶里程却有所不同。

我认为我在基准测试中发现了问题。 对我来说,问题是为每个键急切地分配了元键集。 在我的分支上进行了肮脏的测试后,删除了急切的meta KeySet分配,ksDel()之后内存消耗迅速下降。 也许这是由#3142修复的,因为它更改了我正在谈论的代码。

好的,接下来让我们看看#3142是否可以解决问题!

@ raphi011在#3081之前是否也出现问题?

3142现在已合并。 @ raphi011您可以检查问题是否仍然出现?

不幸的是

对我来说,它似乎至少可以解决/减轻某些问题。 现在,我可以使用约2GB的内存使用数百万个键来运行基准测试,但是由于在同一基准测试中使用了大于20GB的内存,它之前崩溃了。

对我来说,它似乎至少可以解决/减轻某些问题。 现在,我可以使用约2GB的内存使用数百万个键来运行基准测试,但是由于在同一基准测试中使用了大于20GB的内存,它之前崩溃了。

我还没有将它与以前的版本进行比较,但是我很确定它使用的内存比以前少。 今天将检查!

内存仍然没有被释放。

您需要在每个free malloc_trim(0)之后调用free (读取: ksDel )。 这将强制glibc立即将内存返回给OS。 这应该可以解决你们看到的“怪异行为”。 哦,阅读和挖掘glibc很有趣:-)

我创建了#3183以进行进一步的测试/修复。

我发现了一个memleak(在kdbGet返回键中未释放)。

但是越来越多的“ ksClose不能释放532键”可能仅仅是在parentKey中收集的警告。 如果将NUM_RUNS设置为更高,例如100,则警告会限制为100。在某种程度上,它会停滞。 “ ksClose无法释放901键”。 每个句柄都有一个parentKey可以解决此问题。

由#3183关闭

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

mpranj picture mpranj  ·  3评论

mpranj picture mpranj  ·  3评论

markus2330 picture markus2330  ·  4评论

mpranj picture mpranj  ·  4评论

mpranj picture mpranj  ·  3评论