Подтвердить что ты не робот

Что хранится в куче и что хранится в стеке?

Может ли кто-нибудь четко объяснить, с точки зрения C, С++ и Java. Что все идет на стек и что все идет в кучу, и когда выполняется выделение.

Насколько я знаю,

Все локальные переменные, являются ли примитивы, указатели или ссылочные переменные для вызова функции в новом стеке стека.

и все, что создано с новым или malloc, переходит в кучу.

Я запутался в нескольких вещах.

Являются ли ссылки/примитивы, которые являются членами объекта, созданного в куче, также сохраняются в куче?

и как насчет тех локальных членов метода, которые рекурсивно создаются в каждом кадре. Все ли они в стеке. Если да, то это память стека, выделенная во время выполнения? также для литералов, являются ли они частью сегмента кода? и как насчет глобалов в C, статических в С++/Java и статических в C.

4b9b3361

Ответ 1

Структура программы в памяти

Ниже приведена базовая структура любой программы при загрузке в память.

 +--------------------------+
 |                          |
 |      command line        |
 |        arguments         |
 |    (argc and argv[])     |
 |                          |
 +--------------------------+
 | Stack                    |
 | (grows-downwards)        |
 |                          |
 |                          |
 |                          |
 |         F R E E          |
 |        S P A C E         |
 |                          |
 |                          |
 |                          |
 |                          |
 |     (grows upwards) Heap |
 +--------------------------+
 |                          |
 |    Initialized data      |
 |         segment          |
 |                          |
 +--------------------------+
 |                          |
 |     Initialized to       |
 |        Zero (BSS)        |
 |                          |
 +--------------------------+
 |                          |
 |      Program Code        |
 |                          |
 +--------------------------+

Несколько замечаний:

  • Сегмент данных
    • Инициализированный сегмент данных (инициализирован явным инициализатором программистами)
    • Неинициализированный сегмент данных (инициализируется нулевым сегментом данных - BSS [Запуск блока с символом])
  • Сегмент кода
  • Области стека и кучи

Сегмент данных

Сегмент данных содержит глобальные и статические данные, которые явно инициализируются пользователями, содержащими инициализированные значения.

Другая часть сегмента данных называется BSS (из-за того, что старые системы IBM имели этот сегмент, инициализированный до нуля). Это часть памяти, где ОС инициализирует блок памяти нулями. Таким образом, неинициализированные глобальные данные и статические значения получают значение по умолчанию как ноль. Эта область фиксирована и имеет статический размер.

Область данных разделяется на две области на основе явной инициализации, поскольку переменные, которые должны быть инициализированы, могут быть инициализированы один за другим. Однако переменные, которые не инициализируются, не обязательно должны быть явно инициализированы с помощью 0 поочередно. Вместо этого задание инициализации переменной остается в ОС. Эта массовая инициализация может значительно сократить время, необходимое для загрузки исполняемого файла.

В основном макет сегмента данных находится в управлении базовой ОС, но некоторые загрузчики предоставляют частичный контроль пользователям. Эта информация может быть полезна в таких приложениях, как встроенные системы.

Эта область может быть адресована и доступна с помощью указателей из кода. Автоматические переменные имеют накладные расходы при инициализации переменных каждый раз, когда они требуются, и для выполнения этой инициализации требуется код. Однако переменные в области данных не имеют такой перегрузки во время выполнения, поскольку инициализация выполняется только один раз, а также при загрузке.

Сегмент кода

Программный код - это область кода, где исполняемый код доступен для выполнения. Эта область также имеет фиксированный размер. К ним можно обращаться только указатели на функции, а не другие указатели данных. Еще одна важная информация, которую следует отметить здесь, состоит в том, что система может рассматривать эту область как область памяти только для чтения, и любая попытка записи в этой области приводит к поведению undefined.

Постоянные строки могут быть помещены либо в код или область данных, и это зависит от реализации.

Попытка написать в область кода приводит к поведению undefined. Например (я приведу только примеры C), следующий код может привести к ошибке во время выполнения или даже к сбою системы.

int main()
{
    static int i;
    strcpy((char *)main,"something");
    printf("%s",main);
    if(i++==0)
    main();
}

Области стека и кучи

Для исполнения программа использует две основные части: стек и кучу. Фреймы стека создаются в стеке для функций и кучи для распределения динамической памяти. Стек и куча неинициализированы. Поэтому все, что бы там ни было в памяти, становится исходным (мусор) значением для объектов, созданных в этом пространстве.

Давайте посмотрим на примерную программу, чтобы показать, какие переменные будут храниться там, где

int initToZero1;
static float initToZero2;
FILE * initToZero3; 
// all are stored in initialized to zero segment(BSS)

double intitialized1 = 20.0;
// stored in initialized data segment

int main()
{
    size_t (*fp)(const char *) = strlen;
    // fp is an auto variable that is allocated in stack
    // but it points to code area where code of strlen() is stored

    char *dynamic = (char *)malloc(100);
    // dynamic memory allocation, done in heap

    int stringLength;
    // this is an auto variable that is allocated in stack

    static int initToZero4; 
    // stored in BSS

    static int initialized2 = 10; 
    // stored in initialized data segment   

    strcpy(dynamic,"something");    
    // function call, uses stack

    stringLength = fp(dynamic); 
    // again a function call 
}

Или рассмотрим еще более сложный пример,

// command line arguments may be stored in a separate area  
int main(int numOfArgs, char *arguments[])
{ 
    static int i;   
    // stored in BSS 

    int (*fp)(int,char **) = main;  
    // points to code segment 

    static char *str[] = {"thisFileName","arg1", "arg2",0};
    // stored in initialized data segment

    while(*arguments)
        printf("\n %s",*arguments++);

    if(!i++)
        fp(3,str);
}

Надеюсь, это поможет!

Ответ 2

В C/С++: Локальные переменные выделяются в текущем стеке стека (принадлежащем текущей функции). Если вы статически выделяете объект, весь объект выделяется в стеке, включая все его переменные-члены. При использовании рекурсии при каждом вызове функции создается новый стек стека, и все локальные переменные выделяются в стеке. Стек обычно имеет фиксированный размер, и это значение обычно записывается в исполняемый двоичный заголовок во время компиляции/компоновки. Однако это очень специфичная ОС и платформа, некоторые ОС могут динамически наращивать стек при необходимости. Поскольку размер стека обычно ограничен, вы можете выходить из стека при использовании глубокой рекурсии, а иногда даже при отсутствии рекурсии, когда вы статически выделяете большие объекты.

Куча обычно берется как неограниченное пространство (ограниченное только доступной физической/виртуальной памятью), и вы можете выделять объекты в куче, используя malloc/new (и другие функции выделения кучи). Когда объект создается в куче, в нем создаются все его переменные-члены. Вы должны увидеть объект как непрерывную область памяти (эта область содержит переменные-члены и указатель на таблицу виртуальных методов), независимо от того, где она выделена.

Литералы, константы и другие "фиксированные" вещи обычно скомпилированы/связаны в двоичный файл как другой сегмент, поэтому он не является сегментом кода. Обычно вы не можете выделять или освобождать что-либо из этого сегмента во время выполнения. Однако это также зависит от платформы, он может работать по-разному на разных платформах (например, код iOS Obj-C имеет множество постоянных ссылок, вставленных непосредственно в сегмент кода, между функциями).

Ответ 3

В C и С++, по крайней мере, это все для реализации. Стандарты не упоминают "стек" или "кучу".

Ответ 4

В Java локальные переменные могут быть выделены в стек (если не оптимизированы)

Примитивы и ссылки в объекте находятся в куче (поскольку объект находится в куче)

Стек создается заранее, когда поток создается. Он не использует кучу пространства. (Однако создание потока приводит к созданию созданного буфера локального распределения потоков, который немного уменьшает свободную память)

В кучу добавляются уникальные строковые литералы. примитивные литералы могут быть в коде где-нибудь (если не оптимизированы). Является ли поле статичным или не имеет никакого значения.

Ответ 5

Раздел 3.5 Спецификация виртуальной машины Java описывает временные области данных (стеки и куча).

Ни стандарты языка C, ни С++ не указывают, следует ли что-то хранить в стеке или в куче. Они определяют только время жизни объекта, его видимость и изменяемость; это до реализации, чтобы сопоставить эти требования с определенным макетом памяти платформы.

Как правило, все, что выделяется функциями *alloc, находится в куче, а переменные auto и параметры функции находятся в стеке. Строковые литералы могут жить "где-то в другом месте" (они должны быть выделены и видимы в течение всей жизни программы, но попытка их изменения - undefined); на некоторых платформах для их хранения используется отдельный сегмент памяти только для чтения.

Просто помните, что есть некоторые действительно странные платформы, которые могут не соответствовать общей модели кучи стоп-кучи.

Ответ 6

Чтобы ответить на часть вашего вопроса о куче С++ и стеке:

Сначала я должен сказать, что объект, созданный без new, хранится как непрерывный блок в стеке или глобальный в каком-либо глобальном сегменте (специфичный для платформы).

Для объекта, созданного с использованием new в куче, его переменные-члены сохраняются как один непрерывный блок памяти в куче. Это относится к переменным-членам, которые являются примитивами и внедренными объектами. В случае членских варов, которые являются указателями-указателями и ссылочными типами-членами, в объекте сохраняется значение примитивного указателя. То, что указывает это значение, может храниться где угодно (куча, стек, глобальная). Все возможно.

Что касается локальных переменных в методах объекта, они хранятся в стеке, а не в смежных пространствах объектов в куче. Обычно стек создается из фиксированного размера во время выполнения. В каждой теме есть одна. Локальные переменные могут даже не потреблять пространство в стеке, поскольку они могут быть оптимизированы из него (как сказал Пол). Главное, что они не находятся в куче только потому, что они являются функциями-членами объекта в куче. Если они являются локальными переменными типа указателя, они могут быть сохранены в стеке и указывают на что-то в куче или стеке!