Насколько большой может быть malloc в C?

Исключение нулевого указателя генерируется, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  1. Вызов метода экземпляра объекта null.
  2. Доступ или изменение поля объекта null.
  3. Принимая длину null, как если бы это был массив.
  4. Доступ или изменение слотов null, как если бы это был массив.
  5. Бросок null как будто это было значение Throwable.

Приложения должны бросать экземпляры этого класса, чтобы указать на другие незаконные использования объекта null.

Ссылка: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html

26
задан Derek 11 August 2010 в 22:01
поделиться

6 ответов

Наблюдения

Предполагая типичный распределитель, такой как тот, который использует glibc, есть некоторые наблюдения:

  1. Независимо от того, действительно ли используется память, область должна быть зарезервирована непрерывно в виртуальная память.
  2. Наибольшие свободные смежные области зависят от использования памяти существующими областями памяти и доступности этих областей для malloc .
  3. Практика сопоставления зависит от архитектуры и ОС. Кроме того, эти методы влияют на базовые системные вызовы для получения областей памяти (например, malloc , вызывающий mmap для получения страниц).

Эксперимент

Вот простая программа для выделения максимально возможного блока (скомпилируйте с помощью gcc large_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 среда 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

Это выделение ровно 2800 МБ. Наблюдая за соответствующим отображением из / 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. Область пользовательского пространства (3 ГБ в примере)
  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, хотя и с гораздо большими возвращаемыми областями памяти. Другие операционные системы могут отличаться по размещению сопоставлений и обработке больших malloc s, поэтому результаты могут значительно отличаться.

38
ответ дан 28 November 2019 в 06:44
поделиться

На вопрос о malloc дан ответ (зависит от ОС, которую вы не указали), поэтому определение:

#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
ответ дан 28 November 2019 в 06:44
поделиться

Параметр размера в вызове malloc имеет тип size_t, который зависит от реализации. См. этот вопрос для получения дополнительной информации.

1
ответ дан 28 November 2019 в 06:44
поделиться

Это заставило меня задуматься, какое здесь может быть наибольшее значение?

26'901 ^ 2 = 723'663'801. Если ваш double равен 8 байтам, то он меньше 8 ГБ. Я не вижу никаких проблем с выделением такой части памяти, и мои приложения обычно выделяют (в 64-битных системах) гораздо больше. (Наибольшее потребление памяти, которое я когда-либо видел, составляло 420 ГБ (в системе Solaris 10 numa с 640 ГБ ОЗУ) с наибольшим непрерывным блоком ~ 24 ГБ.)

Наибольшее значение трудно определить, поскольку оно зависит от платформы: аналогично 32-битным системам оно зависит от разделения пространства пользователя / пространства ядра. При нынешнем положении дел, я думаю, сначала нужно было бы достичь предела фактической физической памяти - прежде, чем достигнуть предела того, что может выделить libc. (А ядру все равно, оно просто часто расширяет виртуальную память, даже не задумываясь о том, достаточно ли оперативной памяти для ее закрепления.)

1
ответ дан 28 November 2019 в 06:44
поделиться

Самый большой блок памяти, который вы можете запросить malloc () , это наибольшее значение size_t - это SIZE_MAX из . Наибольшая сумма, которую вы можете успешно запросить, очевидно, зависит от операционной системы и конфигурации отдельного компьютера.

Ваш макрос небезопасен. Он выполняет вычисление индекса с помощью переменной int , которая должна иметь только диапазон до 32767. Любое значение выше этого может вызвать подписанное переполнение, что приведет к неопределенному поведению. Вам, вероятно, лучше всего выполнять вычисления как size_t , поскольку этот тип должен содержать любой допустимый индекс массива:

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

(хотя обратите внимание, что если вы укажете отрицательные значения для i ] или j , вы получите индекс, выходящий за границы).

1
ответ дан 28 November 2019 в 06:44
поделиться

Это зависит от вашей реализации malloc!

Согласно Википедии: «Начиная с выпуска v2.3, библиотека GNU C (glibc) использует модифицированный ptmalloc2, который сам основан на dlmalloc v2.7.0». dlmalloc относится к реализации malloc Дуга Ли. В этой реализации важно отметить, что большие маллоки реализуются за счет функциональности файлов, отображаемых в память, поэтому эти блоки действительно могут быть довольно большими без многих проблем с поиском непрерывного блока.

11
ответ дан 28 November 2019 в 06:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: