内存大小随着动态2D内存分配而过度增长

时间:2016-03-14 15:23:59

标签: c arrays linux memory

我有以下示例代码分配1D数组:

#define C 3
int main() {
    int *a;
    long long N = 1000000000, i;
    a = (int*)malloc(sizeof(int) * N * C);
    for (i = 0; i < N * C; i++)
        a[i] = i / 2;
    printf("%d\n", a[N*C - 1]);
    return 0;
}

上面的代码在内存中占用了12 GB的数据 请注意sizeof(int) == 4sizeof(int*) == 8

现在,如果我使用以下代码实现动态2D数组:

#define C 3
int main() {
    int **a;
    long long N = 1000000000, i;
    a = (int**)malloc(sizeof(int*) * N);
    for (i = 0; i < N; i++)
       a[i] = (int*)malloc(sizeof(int) * C);
    for (i = 0; i < N; i++)
       for (j = 0; j < C; j++)
           a[i][j] = i;
    printf("%d\n", a[N-1][C-1]);
    return 0;
}

上面的代码奇怪地占用了大约38 GB的内存(虽然它应该占用12GB + 8GB(指针数组)= 20 GB。

奇怪的是,在第二个示例代码中,如果我将C的值增加到4,5,6,消耗的内存完全相同(38 GB),而对于C=7C=8消耗的内存为54 GB,而C=16消耗的内存为86 GB。这不符合我能想到的任何数学。任何人都可以帮我解决这个问题吗?

1 个答案:

答案 0 :(得分:4)

你的2D数组实际上是一个指向3 int数组的指针数组。所需的额外空间来自3 int的所有小数组中的开销:每个数组使用12个字节加上可能的4到12个字节的填充和估计的至少8个字节的开销。总大小可达32GB + 8GB = 40GB,由top报告为38GiB。根据{{​​1}}的实际实现,开销可以从更少到更多。 malloc返回的内存保证适合最大对齐要求。在intel 64位架构上,这意味着16个字节。如果分配器非常保守,每个小数组占用16个字节,如果不是,它可能占用32个字节或更多。

您可以通过这种方式分配一个真正的2D数组,而不需要任何开销:

malloc

编辑试图解释您的尺寸观察结果:

#define C 3
int main(void) {
    long long N = 1000000000, i;
    int (*a)[C] = malloc(sizeof(*a) * N);
    for (i = 0; i < N; i++) {
        for (j = 0; j < C; j++)
            a[i][j] = i;
    }
    printf("%d\n", a[N-1][C-1]);
    return 0;
}

C=3 to C=6 -> 38GiB C=7, C=8 -> 54 GiB C=16 -> 86 GiB 以GiB显示内存大小,单位为1024x1024x1024字节,比GB小约8%。

指针数组正好使用8GB(80亿字节),开销可以忽略不计。

下表总结了指针数组与top分配给int的大小malloc的各个数组之间的细分:

C

我的解释是:

C used actual arrays pointers total binary --- ---- ------ ------ -------- ----- ------ 3 12 32 32GB 8GB 40GB 37.3GiB 4 16 32 32GB 8GB 40GB 37.3GiB 5 20 32 32GB 8GB 40GB 37,3GiB 6 24 32 32GB 8GB 40GB 37.3GiB 7 28 48 48GB 8GB 56GB 52.2GiB 8 32 48 48GB 8GB 56GB 52.2GiB 16 64 80 80GB 8GB 88GB 82.0GiB 分配的小内存块被四舍五入为16加8的大小倍数,加上竞​​技场簿记信息的额外8字节开销。 malloc返回的地址在16字节边界上对齐,8字节开销位于块之前,块大小是16减8字节的倍数,以允许下一个块对齐。

这可以解释C = 7时从32跳到48字节的跳转。

你应该验证C = 11有类似的跳跃。

您还可以测量C = 2的情况,以查看块大小为

的最小块大小是8还是24字节