4

我有一个基本上可以做到这一点的程序:

  1. 打开一些二进制文件
  2. 向后读取文件(向后,我的意思是它在 EOF 附近开始,并在文件开头结束读取,即“从右到左”读取文件),使用 4MB 块
  3. 关闭文件

我的问题是:为什么内存消耗如下所示,即使我附加的代码中没有明显的内存泄漏?

程序执行期间的内存消耗

以下是为获取上图而运行的程序的源代码:

#include <stdio.h>
#include <string.h>

int main(void)
{
    //allocate stuff
    const int bufferSize = 4*1024*1024;
    FILE *fileHandle = fopen("./input.txt", "rb");
    if (!fileHandle)
    {
        fprintf(stderr, "No file for you\n");
        return 1;
    }
    unsigned char *buffer = new unsigned char[bufferSize];
    if (!buffer)
    {
        fprintf(stderr, "No buffer for you\n");
        return 1;
    }

    //get file size. file can be BIG, hence the fseeko() and ftello()
    //instead of fseek() and ftell().
    fseeko(fileHandle, 0, SEEK_END);
    off_t totalSize = ftello(fileHandle);
    fseeko(fileHandle, 0, SEEK_SET);

    //read the file... in reverse order. This is important.
    for (off_t pos = totalSize - bufferSize, j = 0;
        pos >= 0;
        pos -= bufferSize, j ++)
    {
        if (j % 10 == 0)
        {
            fprintf(stderr,
                "reading like crazy: %lld / %lld\n",
                pos, totalSize);
        }

        /*
         * below is the heart of the problem. see notes below
         */
        //seek to desired position
        fseeko(fileHandle, pos, SEEK_SET);
        //read the chunk
        fread(buffer, sizeof(unsigned char), bufferSize, fileHandle);
    }

    fclose(fileHandle);
    delete []buffer;
}

我也有以下观察:

  1. 尽管 RAM 使用量增加了 1GB,但整个程序在整个执行过程中只使用了 5MB。
  2. 注释调用fread()out使内存泄漏消失。这很奇怪,因为我没有在它附近分配任何东西,这可能会触发内存泄漏......
  3. 此外,正常读取文件而不是向后读取(= 注释调用fseeko()),也会使内存泄漏消失。这是超奇怪的部分

更多信息...

  1. 以下没有帮助:
    1. 检查fread()- 的结果没有任何异常。
    2. 切换到普通、32 位fseekftell.
    3. 做类似的事情setbuf(fileHandle, NULL)
    4. 做类似的事情setvbuf(fileHandle, NULL, _IONBF, *any integer*)
  2. 通过 cygwin 和 mingw 在 Windows 7 上使用 g++ 4.5.3 编译;没有任何优化,只是g++ test.cpp -o test. 两者都存在这种行为。
  3. 测试中使用的文件长 4GB,全是零。
  4. 图表中间的奇怪停顿可以用某种与这个问题无关的临时 I/O 挂断来解释。
  5. 最后,如果我将阅读包装在无限循环中......第一次迭代后内存使用量停止增加。

我认为这与某种内部缓存的建立有关,直到它被整个文件填满。它在幕后是如何运作的?我怎样才能以便携的方式防止这种情况发生?

4

2 回答 2

3

我认为,这更多的是操作系统问题(甚至是操作系统资源使用报告问题),而不是您的程序问题。当然,它只使用 5 MB 内存:1 MB 用于自身(库、堆栈等),4 MB 用于缓冲区。每当您执行 fread() 时,操作系统似乎将文件的一部分“绑定”到您的进程,并且似乎以不同的速度释放它。由于您机器上的内存使用率很低,这不是问题:操作系统只是让已经读取的数据“徘徊”的时间超过了必要的时间,可能假设您的应用程序可能会很快再次读取它,然后它不会必须再次进行绑定。

如果内存压力较高,则操作系统很可能会更快地解除绑定内存,因此您的内存使用历史记录的跳跃会更小。

于 2013-08-11T19:25:01.880 回答
2

我遇到了完全相同的问题,尽管在 Java 中,但在这种情况下并不重要。我通过一次读取更大的块来解决它。我还读取了 4Mb 大小的块,但是当我将其增加到 100-200 Mb 时,问题就消失了。也许它也会为你做到这一点。我在 Windows 7 上。

于 2015-02-02T08:14:01.237 回答