Задумывались ли вы когда-нибудь над тем, как работает распределение памяти в Kotlin? Предлагаю погрузиться в волшебный мир стековой и кучной памяти, используя забавные аналогии из повседневной жизни. Приготовьтесь к увлекательному путешествию! 

Что такое стековая память?

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

В Kotlin стековая память похожа на магазин сэндвичей. Она используется для:

  • локальных переменных: подобно свежеприготовленным сэндвичам, они создаются при вызове функции и исчезают при ее завершении;
  • вызовов функций: каждый вызов функции подобен новому заказу в магазине сэндвичей; шеф-повар (программа) обрабатывает каждый заказ (вызов функции) по очереди, в определенной последовательности.

Рассмотрим небольшой пример:

fun makeSandwich(type: String): String {
val sandwich = "Here's your $type sandwich!" // Локальная переменная, выделяется на стеке
return sandwich
}
fun main() {
val myLunch = makeSandwich("turkey") // Вызов функции, выделяется на стеке
println(myLunch)
}

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

Что такое кучная память?

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

В Kotlin кучная память похожа на этот склад. Она используется для:

  • объектов: подобные товарам, хранящимся на складе, они остаются в памяти до тех пор, пока не будут явно удалены или пока не придет сборщик мусора (волшебная команда уборщиков);
  • глобальных переменных, похожих на товары, доступные из любого места склада (всего приложения).

Вот пример:

class Sandwich(val type: String, val ingredients: List<String>)
fun main() {
val mySandwich = Sandwich("turkey", listOf("turkey", "lettuce", "tomato")) // Объект, выделяется на куче
println("I made a ${mySandwich.type} sandwich with ${mySandwich.ingredients}.")
}

В данном случае mySandwich — это объект, хранящийся в куче. Он останется там до тех пор, пока нужен вам, и будет доступен из разных частей программы.

Разложим все по полочкам: различие памяти стека и кучи

Чтобы все стало еще понятнее, прибегнем к пошаговому объяснению.

1. Вызовы функций и локальные переменные (стек):

  • При вызове функции создается новый «порядок».
  • Локальные переменные внутри функции подобны ингредиентам, используемым для конкретного заказа.
  • После завершения работы функции заказ выполняется, а ингредиенты (локальные переменные) очищаются.

2. Объекты и глобальные переменные (куча):

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

Почему это важно?

Понимание разницы между стековой и кучной памятью помогает писать более эффективный и результативный код на Kotlin. В конечном счете это позволяет лучше управлять ресурсами, избегать утечек памяти и оптимизировать производительность.

Примеры кода: собираем все вместе

Закрепим полученные знания с помощью примеров кода.

Пример стековой памяти

fun prepareOrder(order: String): String {
val preparedOrder = "Order: $order is ready!" // Локальная переменная, стек
return preparedOrder
}
fun main() {
val myOrder = prepareOrder("turkey sandwich") // Вызов функции, стек
println(myOrder)
}

Пример кучной памяти

class WarehouseItem(val name: String, val quantity: Int)
fun main() {
val item = WarehouseItem("Laptop", 50) // Объект, куча
println("Stored ${item.quantity} ${item.name}s in the warehouse.")
}

Заключение

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

Когда будете писать код на Kotlin, вспомните эти забавные аналогии и извлеките максимум пользы из управления памятью.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Sandeep Kella: Stack vs. Heap in Kotlin: Understanding Memory

Предыдущая статьяСоздание снэкбара с обратным отсчетом времени в Android с помощью Jetpack Compose
Следующая статьяAngular: наведение мостов между HttpClient и Signals