3

我编写了一个 c++ openmp 代码,它在并行区域中有一个动态分配的内存私有变量,该区域在 while 循环中。动态分配的内存在每个循环的并行区域结束时被释放。在每次分配和释放之后,我正在通过 linux 机器上的 /proc/self/stat 文件监视内存。我发现驻留集大小的内存更少。为什么会这样?代码类似于 -

float *dynm;
while(condition)
{
    pragma omp parallel shared(list of variables) private(dynm)
    {
        read_values_from_/proc/self/stat_print_rss;
        dynm = new float[size]; 
        read_values_from_/proc/self/stat_print_rss;
        pragma omp for schedule(static, chunk) nowait
                for(counter)
        {
            do_operation;
        }
        delete []dynm;
        read_values_from_/proc/self/stat_print_rss;
    }
}
4

1 回答 1

5

由于计算 RSS 的方式非常复杂,因此测量 RSS 并不是搜索内存泄漏的一种非常准确的方法。有一些特殊的内存调试器,比如valgrind或者内置glibc的可以告诉你内存是否泄漏。您还必须了解glibc使用两种完全不同的机制来动态分配内存。

对于大型分配,它使用mmap(2)系统调用执行私有匿名内存映射。这种方法只能分配大小是系统页面大小的倍数(在大多数现代架构上为 4 KiB)的块,因此不适合小分配。当浪费太多内存时,甚至不适合更大的分配,例如,如果您想分配一个 17 KiB 的块,则必须分配 20 KiB(5 乘以 4 KiB),而 15% 的内存将是浪费了。

对于较小的分配,使用系统提供的堆。有一种叫做系统中断的东西,它是进程的数据段结束的地方。它可以随着brk(2)系统调用向上移动以分配更多内存并向下移动以释放它。由于每个进程只有一个堆区域并且操作系统将其视为单个块,因此内置了一个特殊的堆管理器glibc,可以进一步将该块细分为更小的分配。

C++new运算符调用malloc(3)fromglibc以执行内存分配。malloc(3)根据要分配的内存块的大小调用上述两种内存分配机制之一。C++运算符delete调用free(3)是. 内存块被释放后会发生什么很大程度上取决于它最初是如何分配的。glibcmalloc(3)

使用该mmap(2)机制分配的内存通过使用取消映射来解除分配munmap(2)。这会从进程的虚拟地址空间中删除内存映射,并释放用于支持分配的物理内存页面。

对于在堆中分配的内存而言,事情要复杂得多,并且在很大程度上取决于用于管理它的算法。如果被释放的块不是位于堆的末尾,而是位于其他地方,那么堆大小不能减小,因为在高位内存地址上还有其他分配。这只是所谓的堆碎片表现出来的众多形式之一。另一个没有看到已用内存减少的可能原因是,堆管理器可能决定不将中断的位置移回,以预期未来可能的分配,并且调用brk(2)是一项昂贵的操作。

于 2012-09-20T14:13:10.440 回答