问题描述
我需要从 a space-separated human-readable file 中读取一系列数字并进行一些数学运算,但我在读取文件时遇到了一些非常奇怪的内存行为。
如果我读了数字并立即丢弃它们...
#include <fstream>
int main(int,char**) {
std::ifstream ww15mgh("ww15mgh.grd");
double value;
while (ww15mgh >> value);
return 0;
}
我的程序根据 valgrind 分配了 59MB 的内存,与文件大小成线性比例:
$ g++ stackoverflow.cpp
$ valgrind --tool=memcheck --leak-check=yes ./a.out 2>&1 | grep total
==523661== total heap usage: 1,038,970 allocs,1,970 frees,59,302,487
但是,如果我使用 ifstream >> string
代替,然后使用 sscanf
来解析字符串,我的内存使用看起来更加正常:
#include <fstream>
#include <string>
#include <cstdio>
int main(int,char**) {
std::ifstream ww15mgh("ww15mgh.grd");
double value;
std::string text;
while (ww15mgh >> text)
std::sscanf(text.c_str(),"%lf",&value);
return 0;
}
$ g++ stackoverflow2.cpp
$ valgrind --tool=memcheck --leak-check=yes ./a.out 2>&1 | grep total
==534531== total heap usage: 3 allocs,3 frees,81,368 bytes allocated
为了排除 IO 缓冲区的问题,我尝试了 ww15mgh.rdbuf()->pubsetbuf(0,0);
(这使得程序需要很长时间并且仍然进行 59MB 的分配)和 pubsetbuf
,并分配了大量堆栈缓冲区(仍然是 59MB)。当使用 gcc
10.2.0 中的 /usr/lib/libstdc++.so.6
和 clang
11.0.1 中的 /usr/lib/libc.so.6
在 gcc-libs
10.2.0 和 glibc
2.32 上编译时,行为会重现。系统区域设置为 en_US.UTF-8
,但如果我设置环境变量 LC_ALL=C
,这也会重现。
我第一次发现问题的 ARM CI 环境是在 Ubuntu Focal 上使用 GCC 9.3.0、libstdc++6
10.2.0 和 libc
2.31 交叉编译的。
在 advice in the comments 之后,我尝试了 LLVM 的 libc++ 并使用原始程序获得了完全正常的行为:
$ clang++ -std=c++14 -stdlib=libc++ -I/usr/include/c++/v1 stackoverflow.cpp
$ valgrind --tool=memcheck --leak-check=yes ./a.out 2>&1 | grep total
==700627== total heap usage: 3 allocs,8,664 bytes allocated
因此,这种行为似乎是 GCC 的 fstream
实现所独有的。在构建或使用 ifstream
时,我是否可以做一些不同的事情来避免在 GNU 环境中编译时分配大量堆内存?这是他们的 <fstream>
中的错误吗?
正如在评论讨论中发现的那样,程序的实际内存占用是完全正常的(84kb),它只是分配和释放相同的一小部分内存数十万次,这在使用像 ASAN 这样的自定义分配器时会产生问题避免重复使用堆空间。我发了 a follow-up question 询问如何在“ASAN”级别处理此类问题。
一个 gitlab project that reproduces the issue in its CI pipeline 由 Stack Overflow 用户 @KamilCuk 慷慨提供。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)