Быстрое понимание стека / кучи
Я хочу понять, что хранится в стеке / куче в Swift. У меня есть приблизительная оценка: все, что вы печатаете и адрес памяти, - это не значения, они хранятся в стеке, и то, что распечатывается как значения, они находятся в куче, в основном в соответствии со значениями и ссылочными типами. Я совершенно не прав? И, возможно, вы могли бы представить визуальное представление стека / кучи?
4 ответа
Как уже было сказано, ссылочные типы хранятся в куче, а значения в стеке…
Здесь я хочу дать объяснение, почему...
Стек и куча?
Стек используется для статического выделения памяти и Heap для динамического выделения памяти, оба хранятся в оперативной памяти компьютера.
Переменные, размещенные в стеке, хранятся непосредственно в памяти, и доступ к этой памяти очень быстрый, и с ее распределением происходит компиляция программы. Когда функция или метод вызывает другую функцию, которая, в свою очередь, вызывает другую функцию и т. Д., Выполнение всех этих функций приостанавливается до тех пор, пока самая последняя функция не вернет свое значение. Стек всегда резервируется в порядке LIFO, последний зарезервированный блок всегда является следующим блоком, который должен быть освобожден. Это действительно упрощает отслеживание стека, освобождение блока из стека - всего лишь настройка одного указателя.
Переменным, выделенным в куче, выделяется их память во время выполнения, и доступ к этой памяти немного медленнее, но размер кучи ограничен только размером виртуальной памяти. Элемент кучи не имеет зависимостей друг от друга и всегда может быть доступен случайным образом в любое время. Вы можете выделить блок в любое время и освободить его в любое время. Это значительно усложняет отслеживание того, какие части кучи выделены или свободны в любой момент времени.
Классы (ссылочные типы) размещаются в куче, типы значений (такие как Struct, String, Int, Bool и т. Д.) Находятся в стеке. Посмотрите эту тему для более подробных ответов: Почему стоит выбрать Struct Over Class?
Стек против кучи
Stack
является частью потока. Он состоит из фреймов методов (функций) в порядке LIFO. Фрейм метода содержит локальные переменные. Фактически это трассировка стека методов, которую вы видите во время отладки или анализа ошибки <sup>[О программе]</sup> . Создается новая копия ценности - это может быть копия
reference type
адрес или копия
value type
(Копирование по механизму записи). Потоковая безопасность <sup>[О программе]</sup>
Heap
другая часть памяти, где ARC <sup>[About]</sup> вступает в игру. Здесь требуется больше времени, чтобы выделить память (найти подходящее место и распределить его синхронно). Создана новая копия справки
Эти концепции такие же, как [иллюстрация JVM]
Xcode предлагает следующий вариант с использованием
Debug Memory Graph
* Чтобы увидеть использование Backtrace
Malloc Stack Logging
Обычно, когда мы задаем такой вопрос (это стек или куча), мы заботимся о производительности и мотивированы желанием избежать чрезмерных затрат на выделение кучи. Следование общему правилу о том, что «ссылочные типы размещаются в куче, а типы значений — в стеке», может привести к неоптимальным проектным решениям и требует дальнейшего обсуждения.
Можно ошибочно заключить, что передача структур (типов значений) всегда быстрее, чем передача классов (ссылочных типов), потому что она никогда не требует выделения кучи. Оказывается, это не всегда верно.
Важным контрпримером являются типы протоколов, в которых конкретные полиморфные типы с семантикой значений (структуры) реализуют протокол, как в этом игрушечном примере:
protocol Vehicle {
var mileage: Double { get }
}
struct CombustionCar: Vehicle {
let mpg: Double
let isDiesel: Bool
let isManual: Bool
var fuelLevel: Double // gallons
var mileage: Double { fuelLevel * mpg }
}
struct ElectricCar: Vehicle {
let mpge: Double
var batteryLevel: Double // kWh
var mileage: Double { batteryLevel * mpge / 33.7 }
}
func printMileage(vehicle: Vehicle) {
print("\(vehicle.mileage)")
}
let datsun: Vehicle = CombustionCar(mpg: 18.19,
isDiesel: false,
isManual: false,
fuelLevel: 12)
let tesla: Vehicle = ElectricCar(mpge: 132,
batteryLevel: 50)
let vehicles: [Vehicle] = [datsun, tesla]
for vehicle in vehicles {
printMileage(vehicle: vehicle)
}
Обратите внимание, что
Оказывается, под капотом довольно много логики. Компилятор Swift создаст так называемый файл . Это структура данных фиксированного размера, действующая как оболочка вокруг объекта. Именно этот контейнер передается вызову функции (помещается в стек) вместо фактической структуры.
| |
|valueBuffer|
| |
| vwt |
| pwt |
Первые три слова называются the, и именно здесь сохраняется фактическая структура. Ну, это если размер структуры больше трех слов - в таком случае компилятор выделит структуру в куче и сохранит ссылку на нее в
STACK STACK HEAP
| mpge | | reference |-->| mpg |
|batteryLevel| | | | isDiesel |
| | | | | isManual |
| vwt | | vwt | | fuelLevel |
| pwt | | pwt |
Таким образом, передача объекта типа протокола, подобного этому, в функцию может фактически потребовать выделения кучи. Компилятор выполнит распределение и скопирует значения, поэтому вы все равно получите семантику значений, но стоимость будет варьироваться в зависимости от того, состоит ли ваша структура из 3 слов или более. Это делает «типы значений в стеке, ссылочные типы в куче» не всегда правильными.