web-dev-qa-db-ja.com

Cでmallocはどのくらい大きくできますか?

26901 ^ 2 * sizeof(double)のCのmallocがあります

これは私にここで最大の価値が何であるかを考えさせましたか?

また、この2D配列にアクセスするためのマクロの定義に問題がありますか?

 #define DN(i,j) ((int)i * ny + (int)j)

これは私にとってうまくいっていないようです-または少なくとも私はそれが確かではありません。 A [DN(indx、jndx)]が実際に何を見ているのかを伝えるために、マクロでtotalviewを実行する方法を理解できません。

27
Derek

観察

Glibcが使用するような典型的なアロケータを想定すると、いくつかの観察があります:

  1. メモリが実際に使用されているかどうかに関係なく、領域は仮想メモリ内で連続して予約する必要があります。
  2. 最大の空き連続領域は、既存のメモリ領域のメモリ使用量、および malloc に対するそれらの領域の可用性に依存します。
  3. マッピングの方法は、アーキテクチャとOSによって異なります。さらに、メモリ領域を取得するための基礎となるシステムコールは、これらの方法(ページを取得するためのmallocから mmap への呼び出しなど)の影響を受けます。

実験

これが 簡単なプログラム であり、可能な最大のブロックを割り当てます(gcc largest_malloc_size.c -Wall -O2でコンパイルします:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static void *malloc_wrap(size_t size)
{
    void *p = malloc(size);
    if (p) {
        printf("Allocated %zu bytes from %p to %p\n", size, p, p + size);
    }
    else {
        printf("Failed to allocated %zu bytes\n", size);
    }
    return p;
}

int main()
{
    size_t step = 0x1000000;
    size_t size = step;
    size_t best = 0;
    while (step > 0)
    {
        void *p = malloc_wrap(size);
        if (p) {
            free(p);
            best = size;
        }
        else {
            step /= 0x10;
        }
        size += step;
    }
    void *p = malloc_wrap(best);
    if (p) {
        pause();
        return 0;
    }
    else {
        return 1;
    }
}

上記のプログラム(./a.out)をLinux stanley 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linuxマシンで実行すると、次の結果が得られます。

<snip>
Allocated 2919235584 bytes from 0x9763008 to 0xb7763008
Allocated 2936012800 bytes from 0x8763008 to 0xb7763008
Failed to allocated 2952790016 bytes
Failed to allocated 2953838592 bytes
Failed to allocated 2953904128 bytes
Failed to allocated 2953908224 bytes
Allocated 2936012800 bytes from 0x85ff008 to 0xb75ff008

これは正確に2800MiBの割り当てです。 /proc/[number]/maps から関連するマッピングを確認します。

<snip>
0804a000-0804b000 rw-p 00001000 08:07 3413394    /home/matt/anacrolix/public/stackoverflow/a.out
085ff000-b7600000 rw-p 00000000 00:00 0          [heap]
b7600000-b7621000 rw-p 00000000 00:00 0 
b7621000-b7700000 ---p 00000000 00:00 0 
b7764000-b7765000 rw-p 00000000 00:00 0 
b7765000-b78b8000 r-xp 00000000 08:08 916041     /lib/tls/i686/cmov/libc-2.11.1.so
<snip>
bfc07000-bfc1c000 rw-p 00000000 00:00 0          [stack]

結論

プログラムデータとコードの間の領域、および ユーザー/カーネルメモリ空間の境界 (明らかにこのシステムでは3G/1G)にぴったり合う共有ライブラリマッピングの領域でヒープが拡張されているようです。

この結果は、mallocを使用して割り当て可能な最大スペースがおおよそ次の値に等しいことを示唆しています。

  1. ユーザー空間領域(例では3GB)
  2. ヒープの開始までのオフセット(プログラムコードとデータ)を減らす
  3. メインスレッドスタック用に予約されているスペースが少ない
  4. 共有ライブラリにマップされたすべてのオブジェクトが占めるスペースが少なくなります
  5. 最後に、ヒープに利用可能な領域内で、基本的なシステムコールによって見つけられる最大の連続した領域(他のマッピングによってフラグメント化される可能性があります)

ノート

GlibcとLinuxの実装に関しては、次の手動スニペットが非常に重要です。

malloc

   Normally, malloc() allocates memory from the heap, and adjusts the size
   of the heap as required, using sbrk(2).  When allocating blocks of mem‐
   ory larger than MMAP_THRESHOLD bytes, the glibc malloc() implementation
   allocates the memory as a  private  anonymous  mapping  using  mmap(2).
   MMAP_THRESHOLD  is  128  kB  by  default,  but is adjustable using mal‐
   lopt(3).

mmap

   MAP_ANONYMOUS
          The mapping is not backed by any file; its contents are initial‐
          ized to zero.

あとがき

このテストはx86カーネルで行われました。非常に大きなメモリ領域が返されますが、x86_64カーネルからも同様の結果が期待されます。他のオペレーティングシステムでは、マッピングの配置や大きなmallocsの処理が異なるため、結果がかなり異なる場合があります。

38
Matt Joiner

それはあなたのmalloc実装に依存します!

Wikipediaによると、「v2.3リリース以降、GNU Cライブラリ(glibc)は、それ自体がdlmalloc v2.7.0に基づく変更されたptmalloc2を使用します。」dlmallocは、Doug Leaのmallocを指します。この実装で注意すべき重要な点は、大きなmallocはオペレーティングシステムのメモリマップファイル機能によって実現されるため、これらのブロックは、隣接するブロックを見つけるという多くの問題がなくても非常に大きくなる可能性があります。

11
Hut8

Mallocの質問に回答します(指定しないOSに依存します)。

_#define DN(i,j) ((int)i * ny + (int)j)
_

誰かがDN(a+b,c)を実行する可能性があるため、安全ではありません

_((int)a+b * ny + (int)c)
_

これはおそらくあなたが望んでいたものではありません。そこにたくさんの括弧を入れてください:

_#define DN(i,j) ((int)(i) * ny + (int)(j))
_

DN(indx,jndx)が何を指しているかを確認するには、printf("%d\n",DN(indx,jndx));

7
mvds

できる最大のメモリブロックaskmalloc()は、最大のsize_t値です。これは、SIZE_MAXからの<limits.h>です。あなたができる最大の量成功リクエストは明らかにオペレーティングシステムと個々のマシンの設定に依存しています。

マクロは安全ではありません。これは、int変数を使用してインデックス計算を実行します。これは、最大32767の範囲である必要があります。これより大きい値を指定すると、符号付きオーバーフローが発生し、動作が未定義になる可能性があります。その型は有効な配列インデックスを保持できる必要があるため、size_tとして計算を行うのが最善の方法です。

#define DN(i, j) ((size_t)(i) * ny + (size_t)(j))

(ただし、iまたはjに負の値を指定すると、範囲外のインデックスが取得されます)。

1
caf

Mallocへの呼び出しのサイズパラメータは、サイズの種類で、実装によって異なります。詳細は この質問 を参照してください。

1
Will

これは私にここで最大の価値が何であるかを考えさせましたか?

26'901 ^ 2 = 723'663'801。 doubleが8バイトの場合、8GB未満です。私はメモリの多くを割り当てることにまったく問題がないことを確認し、私のアプリは(64ビットシステムで)日常的にはるかに多く割り当てます。 (私が今まで見た中で最大のメモリ消費量は420GB(640GB RAMを搭載したSolaris 10 numaシステム上)で、最大24GBの連続ブロックがありました。)

最大値はプラットフォームに依存するため、特定が困難です。32ビットシステムと同様に、ユーザー空間/カーネル空間の分割に依存します。現状では、実際の物理的な限界に最初に到達すると思いますRAM-libcが割り当てることができる限界に到達する前に(そしてカーネルは気にしません、十分なRAMそれを固定するためにあるかどうか)さえ考慮せずに仮想メモリを拡張するだけです。

1
Dummy00001