我需要从一个由空格分隔的可读文件中读取一系列数字并进行数学运算,但在读取文件时遇到了一些非常奇怪的内存行为。
如果我读取这些数字并立即丢弃它们...
为了排除IO缓冲区的问题,我尝试了
所以,这种行为似乎是GCC的
正如在评论讨论中发现的那样,程序的实际内存占用是完全合理的(84kb),它只是分配和释放了数十万次相同的小内存块,这在使用像ASAN这样的自定义分配器时会创建问题,因为它避免重新使用堆空间。我发布了后续问题,询问如何应对这种“ASAN”级别的问题。 @KamilCuk Stack Overflow用户慷慨地贡献了一个在其CI管道中重现该问题的gitlab项目。
如果我读取这些数字并立即丢弃它们...
#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,038,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和clang
11.0.1编译时,使用来自gcc-libs
10.2.0的/usr/lib/libstdc++.so.6
和glibc
2.32的/usr/lib/libc.so.6
,行为都会重现。系统语言环境设置为en_US.UTF-8
,但如果我设置环境变量LC_ALL=C
,这也会重现。
我最初在Ubuntu Focal上交叉编译,使用GCC 9.3.0,libstdc++6
10.2.0和libc
2.31,我在ARM CI环境中首次注意到此问题。
按照评论中的建议,我尝试了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, 3 frees, 8,664 bytes allocated
所以,这种行为似乎是GCC的
fstream
实现独有的。在GNU环境下编译时,我是否可以在构建或使用ifstream
时采取不同的方法来避免分配大量堆内存?这是他们<fstream>
中的一个错误吗?正如在评论讨论中发现的那样,程序的实际内存占用是完全合理的(84kb),它只是分配和释放了数十万次相同的小内存块,这在使用像ASAN这样的自定义分配器时会创建问题,因为它避免重新使用堆空间。我发布了后续问题,询问如何应对这种“ASAN”级别的问题。 @KamilCuk Stack Overflow用户慷慨地贡献了一个在其CI管道中重现该问题的gitlab项目。
istream
类的operator >>(double&)
实现中出现了内存泄漏……仍然不知道如何修复。:( - CiaPan