【问题标题】:Linux memory management overheadLinux 内存管理开销
【发布时间】:2014-07-10 17:49:04
【问题描述】:

我试图解释我在 Linux 中的应用程序占用的内存。我做了一个基本的测试,发现如果我们新建一些内存,它会为单个新内存分配至少 32 个字节。

这是我的代码。

#include <iostream>
#include <stdlib.h>

using namespace std;

int main(int argc, const char** argv)
{
        int iBlockSize = atoi(argv[1]);
        int iBlockCount = atoi(argv[2]);

        for (int i = 0 ; i < iBlockCount ; i++)
        {
                cout << (int*)(new char[iBlockSize]) << endl;
        }
        return 0;
};

当我执行./a.out 8 100 时,它给出了以下结果。

....
....
....
0xf6db10
0xf6db30
0xf6db50
0xf6db70
0xf6db90
0xf6dbb0
0xf6dbd0
0xf6dbf0
0xf6dc10
0xf6dc30
0xf6dc50
0xf6dc70

我得到的所有内存都有 32 个字节的间隙。

直到 24 (BlockSize) 它是一样的。如果超过 24 则为 48 字节。

./a.out 25 100

....
....
....
0x18b30c0
0x18b30f0
0x18b3120
0x18b3150
0x18b3180
0x18b31b0
0x18b31e0
0x18b3210
0x18b3240
0x18b3270
0x18b32a0

当我测试更大尺寸时;发现我们获得的内存增加了 16 字节块,保持至少 8 字节开销。

我的问题是,

  1. 我的测试正确吗?
  2. 这是 linux 内存管理的正确行为吗?
  3. 如果我们新的 8 个字节,我们得到 32。其他 24 发生了什么?重复使用或分散开销?

【问题讨论】:

  • 每次分配都会有开销。并且分配数组通常需要将大小存储在某个地方,通常存储在数据本身附近。

标签: c++ linux memory


【解决方案1】:

类 C 语言中的内存分配必须为所有原始类型返回适当对齐的 [0] 内存。 这意味着内存分配通常会为您提供至少 8 字节对齐的内存,以便您可以在其中存储 doubles

因此,当您请求 1 个字节的内存时,由于对齐要求,您将至少使用 8 个字节。 在 64 位系统上,内存分配通常为您提供 16 字节对齐的内存,因为这些系统通常具有 16 字节的大类型(SSE 向量)

此外,内存分配器需要一些空间来存储其管理数据,例如分配的大小。 根据实现的不同,这些数据可以放在用户分配的内存块之前或之后。

[0] 当地址/指针是它被访问的大小的倍数时,内存是对齐的,一些 cpu 不支持非对齐访问(例如 sparc),如果你这样做,其他的(比如 x86)可能会有性能损失.

【讨论】:

  • 感谢 jtaylor 的解释。无论如何,如果内存元数据(管理数据)也保存在同一位置。如果我们误写越界,会不会影响到内存管理模块?
  • 我用新的 char[8] 分配的全部 32 个字节的 memset 测试了相同的代码。然后程序在 delete[] (*** glibc detected *** ./a.out: free(): invalid next size (fast): 0x0000000000603fa0 ***) 处崩溃,并且不在 memset 中。这说明内存管理数据也与内存本身保持一致。如果我删除 delete[],即使分配和写入千兆字节的内存,程序也不会出现任何错误。
  • 管理数据的保存位置是一个实现细节,并且会根据您使用的 malloc 进行更改。例如,glibc 具有特殊的 malloc 调试标志(例如,默认情况下通常启用 MALLOC_CHECK_),这将导致它检查其内部数据是否存在不一致,这就是您在删除时看到崩溃但在 memset 上看不到的原因。
【解决方案2】:

C++ 标准中与该主题相关的几行;我认为它简要描述了这不是错误的行为。

在 C++ 程序中定义的任何分配和/或释放函数, 包括库中的默认版本,应符合 3.7.4.1 和 3.7.4.2 中指定的语义。 ...

3.7.4.1 分配函数 ...返回的指针应适当对齐,以便它可以转换为任何指针 具有基本对齐要求的完整对象类型 (3.11) 和...

3.11 对齐 对象类型具有对齐要求(3.9.1、3.9.2),这些要求限制了可以分配该类型对象的地址。对齐是实现定义的 整数值,表示连续之间的字节数 可以分配给定对象的地址。 ...

【讨论】:

    【解决方案3】:

    我找到了这篇文章

    https://software.intel.com/en-us/articles/align-and-organize-data-for-better-performance

    它说“如果数据将通过向量指令加载和存储来访问,请在 16 字节边界上对齐数据。”

    所以看起来这是 glibc 做出的对齐决定

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-23
      • 1970-01-01
      • 2011-10-27
      • 2011-11-29
      • 1970-01-01
      • 2020-06-13
      • 1970-01-01
      相关资源
      最近更新 更多