Куча (динамическая память)
Память, которую программа запрашивает во время работы через malloc или new, берётся из кучи. В микроконтроллере куча делит с стеком одну и ту же свободную часть ОЗУ и растёт ему навстречу, поэтому ошибки с ней приводят к порче данных без всякого предупреждения.
Обновлено Редакция GAW
Куча (heap, динамическая память) — область ОЗУ, из которой программа во время работы получает память вызовами malloc, calloc или оператором new и возвращает через free или delete. В отличие от переменных в секциях .data и .bss, размер которых известен при компоновке, размер кучи меняется по ходу работы.
Где она в ОЗУ
avr-libc описывает типичную раскладку: в начале ОЗУ .data, за ней .bss, куча — сразу после конца .bss, а стек начинается с вершины ОЗУ и растёт вниз. Куча и стек растут навстречу друг другу, и аппаратной защиты между ними нет. Шаблон ST для STM32F103 устроен так же: функция _sbrk выдаёт память, начиная с символа end, и не поднимается выше текущего указателя стека.
| avr-libc | STM32F103: шаблон ST и syscalls.c STM32CubeF1 | |
|---|---|---|
| Начало кучи | __heap_start, после .bss | end, после .bss |
| Предел | стек минус __malloc_margin (32 байта) | текущий указатель стека |
| Проверка при сборке | — | _Min_Heap_Size 0x200, _Min_Stack_Size 0x400 |
Почему кучу избегают
- Фрагментация. После выделения и освобождения блоков разного размера свободная память дробится, и большой запрос не помещается ни в одну дыру, хотя суммарно места хватает.
- Столкновение со стеком. Глубокая вложенность вызовов или прерываний может залезть в область кучи; avr-libc оставляет запас всего 32 байта на всё, что может вложиться в текущую функцию.
- Накладные расходы. У avr-libc каждый блок несёт 2-байтовый заголовок с размером.
Поэтому в прошивках буферы обычно объявляют статически, а malloc вызывают разве что при старте.
Не путать кучу со стеком: стек хранит локальные переменные и адреса возврата и освобождается автоматически при выходе из функции, а блок кучи живёт, пока его не освободят. Как задать и проверить размеры кучи и стека — в статье «Скрипт компоновщика GNU ld», типы и распределение памяти в C — в статье «Язык C для микроконтроллеров».
Частые вопросы
Где находится куча в ОЗУ микроконтроллера?
Сразу после .bss, а стек начинается с вершины ОЗУ и растёт вниз — навстречу куче. Так описывает раскладку avr-libc, так же устроен шаблон ST для STM32F103. Аппаратной границы между ними нет: если куча и стек встретятся, они начнут портить друг друга.
Что такое фрагментация кучи?
После многих выделений и освобождений блоков разного размера свободная память оказывается разбита на мелкие «дыры». Суммарно места хватает, но ни одна дыра не вмещает новый запрос, и malloc возвращает NULL или куча растёт навстречу стеку. avr-libc прямо называет фрагментацию причиной столкновения кучи со стеком.
Сколько памяти под кучу в проекте STM32?
В шаблоне скрипта компоновщика ST — минимум 0x200 байт (512) под кучу и 0x400 (1024) под стек. Это не ограничение, а проверка: компоновщик выдаст ошибку, если после .bss не осталось хотя бы этого. Реально куча растёт, пока не упрётся в указатель стека — так написана функция _sbrk в примерах STM32CubeF1; в других шаблонах предел может быть другим.
Источники
- AVR-LibC 2.3.2: Memory Areas and Using malloc() (malloc.dox), util/ram-usage.h — стандартная раскладка ОЗУ: .data, затем .bss, куча после конца .bss, стек сверху вниз; аппаратной защиты нет; куча и стек могут столкнуться, в том числе из-за фрагментации; malloc не поднимается выше текущего стека минус __malloc_margin, по умолчанию 32 байта; у каждого блока 2-байтовый заголовок с размером; _get_ram_unused несовместима с malloc
- STMicroelectronics. cmsis-device-f1, коммит c8e9a4a: Source/Templates/gcc/linker/STM32F103XB_FLASH.ld — _Min_Heap_Size = 0x200, _Min_Stack_Size = 0x400; секция ._user_heap_stack проверяет, что после .bss остаётся место под кучу и стек
- STMicroelectronics. STM32CubeF1, коммит c7656f1: syscalls.c примеров — _sbrk: куча начинается с символа end и не поднимается выше текущего указателя стека
Нашли ошибку?
Смотрите также
- Скрипт компоновщика GNU ldMEMORY, SECTIONS, VMA и LMA, KEEP, стек и куча, map-файл, переполнение
- СтекОбласть ОЗУ для адресов возврата и локальных переменных
- Секции .data и .bssГде лежат глобальные переменные: копирование и обнуление при старте
- C для микроконтроллеровТипы, stdint, volatile, атомарность, регистры, константы во Flash