我是 c++初学者,尝试使用多进程时遇到了诡异的内存泄漏的问题,这是全部代码。
在我本地的服务器用 valgrind 测试没有泄漏,但另一台服务器上就会出现泄漏问题,我百思不得其解,请大神给点思路
随附 cpp 源文件和 valgrind 的 log 的下载链接:
#include <string.h> #include <regex> #include <sys/wait.h> #include <unistd.h>
int test(std::string input, std::string output){ int status = 0;
pid_t pid; for (int i = 0; i < 10; i++) { pid = fork(); if (pid == 0) { exit(0); } else if (pid < 0) { perror("fork"); exit(1); } else { waitpid(pid, &status, 0); } } // memery leak return 0;
}
int main(int argc, char *argv[]) { std::string input = "1"; std::string output = "2";
test(input, output); return 0;
}
1 vsyf 2022-09-11 10:32:07 +08:00 ![]() 可以看到没有泄露 ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) still reachable 的应该是 2 个 string ,和标准库的实现有关吧。 https://valgrind.org/docs/manual/faq.html#faq.reports |
![]() | 2 codyfeng 2022-09-11 11:28:44 +08:00 ![]() 一个可能性:旧版 gcc 的 std::string 用的 copy-on-write ,多个 copy 用的是同一个 buffer ,不是线程安全的。可以将尝试`test(input, output);`改为`test(input.c_str(), output.c_str());`看看是否能解决 |
![]() | 3 48y1951r9G8k7Zou 2022-09-11 12:06:44 +08:00 ![]() 我在自己开发机上面测试没有复现 楼主还是发下出问题的 gcc 和 libstdc++ 版本吧 |
4 ShinomiyaKaguya OP |
![]() | 5 BrettD 2022-09-11 12:51:44 +08:00 via iPhone 在出问题的环境里, _GLIBCXX_USE_CXX11_ABI 宏的默认值是多少? |
6 ShinomiyaKaguya OP @BrettD 有没有什么命令来查看默认值,我没查到怎么看 |
![]() | 7 BrettD 2022-09-11 13:10:08 +08:00 via iPhone 最简单方法就直接写一段 C++代码,用#ifdef 检查有没有定义这个宏,如果有的话用 cout 输出这个宏 |
8 ShinomiyaKaguya OP @BrettD 0 |
![]() | 9 BrettD 2022-09-11 13:38:22 +08:00 via iPhone 那有可能是 2 楼说的 CoW String 原因,试一下编译参数-D_GLIBCXX_USE_CXX11_ABI=1 再试 |
10 ShinomiyaKaguya OP @BrettD 我用这条命令编译,还是内存泄漏,但现在改用 char[]就没问题了 g++ -Wall -Werror -std=c++14 -D_GLIBCXX_USE_CXX11_ABI=1 -O -o task2 task2.cpp |
![]() | 11 BrettD 2022-09-11 16:19:31 +08:00 你的系统是 RHEL 6 或者 7 吗?在 RHEL 6 和 7 上,_GLIBCXX_USE_CXX11_ABI 是被强制禁用的,即使手动设定了这个宏,编译出来的程序还是使用的旧 ABI 下的 COW 的 std::string ,可能导致了 Valgrind 报告 still reachable 。如果在 RHEL 8 或者更新的系统上面启用_GLIBCXX_USE_CXX11_ABI ,就没有这个报告了。 |
![]() | 12 BrettD 2022-09-11 16:20:22 +08:00 我在 Rocky Linux 9 上面手动制定-D_GLIBCXX_USE_CXX11_ABI=0 成功复现了这个问题,默认情况下_GLIBCXX_USE_CXX11_ABI=1 就没有这个问题。 |
![]() | 13 BrettD 2022-09-11 16:28:40 +08:00 ![]() 如果在你的 test 函数中把 if (pid == 0) { exit(0); } 改成 if (pid == 0) { return 0; } Valgrind 就不再报告 still reachable 了。 |
![]() | 14 BrettD 2022-09-11 16:30:58 +08:00 如果你使用 exit(0)退出进程,main 函数中的 string 对象的析构函数就不会被调用,所以可能导致了“内存泄漏”。如果改成 return 0 ,控制流会回到 main 函数,然后在 main 函数 return 0 之后,main 函数中的 string 对象的析构函数被调用。 |
15 ShinomiyaKaguya OP ![]() |
![]() | 16 BrettD 2022-09-11 17:04:13 +08:00 via iPhone 你再仔细看一遍,我说的是 RHEL 6/7 是禁用 CXX11ABI ,我是 Rocky 9 ,和 RHEL 9 等效,RHEL 8/9 是可以使用 CXX11ABI 的 |
17 ShinomiyaKaguya OP @BrettD 抱歉,是 RHEL6/7 禁用,可能我的系统和 RHEL 一样禁止修改了这个参数 |