我是 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 1
可以看到没有泄露
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 1
一个可能性:旧版 gcc 的 std::string 用的 copy-on-write ,多个 copy 用的是同一个 buffer ,不是线程安全的。可以将尝试`test(input, output);`改为`test(input.c_str(), output.c_str());`看看是否能解决
|
3
majula 2022-09-11 12:06:44 +08:00 1
我在自己开发机上面测试没有复现
楼主还是发下出问题的 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 1
如果在你的 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 一样禁止修改了这个参数
|