Что такое память на стеке?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Память на стеке (Stack Memory)
Память на стеке (Stack) — это автоматически управляемая область памяти, которая работает по принципу LIFO (Last In First Out). В стеке хранятся примитивные значения и ссылки на объекты, а также информация о вызванных методах.
Как работает стек
Когда вызывается метод, для него создаётся stack frame (кадр стека), который содержит:
- Локальные переменные
- Параметры метода
- Адрес возврата
Когда метод заканчивается, его frame удаляется из стека:
fun main() {
val a = 10 // frame main: [a=10]
methodOne(a) // frame methodOne добавлена в стек
println(a) // frame methodOne удалена, остаётся main
}
fun methodOne(x: Int) {
val b = x + 5 // frame methodOne: [x=10, b=15]
methodTwo(b) // frame methodTwo добавлена
} // frame methodOne удалена
fun methodTwo(y: Int) {
val c = y * 2 // frame methodTwo: [y=15, c=30]
} // frame methodTwo удалена
Что хранится в стеке
1. Примитивные типы (полностью):
val intValue: Int = 42 // Весь int в стеке
val floatValue: Float = 3.14f // Весь float в стеке
val boolValue: Boolean = true // Весь boolean в стеке
val charValue: Char = 'A' // Весь char в стеке
2. Ссылки на объекты (только ссылка):
val user: User = User("John") // Ссылка на объект в стеке
// Сам объект User в HEAP
val numbers: List<Int> = listOf(1, 2, 3) // Ссылка на List в стеке
// Сам List объект в HEAP
3. Локальные переменные методов:
fun calculateSum(a: Int, b: Int): Int {
val sum = a + b // Локальная переменная sum в стеке
val multiplied = sum * 2 // Локальная переменная multiplied в стеке
return multiplied
}
// Когда метод завершится, sum и multiplied удаляются из стека
Стек vs Heap
| Аспект | Стек (Stack) | Куча (Heap) |
|---|---|---|
| Управление памятью | Автоматическое (LIFO) | Управление сборщиком мусора |
| Что хранится | Примитивы, ссылки, локальные переменные | Объекты, массивы, строки |
| Скорость | Очень быстро | Медленнее чем стек |
| Размер | Ограничен (обычно МБ) | Больше, чем стек |
| Многопоточность | Каждый поток имеет свой стек | Общая куча для всех потоков |
| Ошибка переполнения | StackOverflowError | OutOfMemoryError |
Пример с диаграммой памяти
class User(val name: String, val age: Int)
fun main() {
val user1 = User("Alice", 25) // СТЕК: user1 -> адрес объекта
// HEAP: User("Alice", 25)
val user2 = user1 // СТЕК: user2 -> тот же адрес
// HEAP: User("Alice", 25) (один объект!)
val numbers = listOf(1, 2, 3) // СТЕК: numbers -> адрес List
// HEAP: List([1,2,3])
}
// После завершения main:
// - СТЕК: очищается (user1, user2, numbers удаляются)
// - HEAP: объекты помечаются для garbage collection
StackOverflowError
Ошибка возникает при переполнении стека, обычно из-за бесконечной рекурсии:
// ❌ StackOverflowError
fun recursiveWithoutBase(n: Int): Int {
return recursiveWithoutBase(n + 1) // Нет условия выхода!
}
// Каждый вызов добавляет frame в стек
// Стек переполняется -> StackOverflowError
// ✅ Правильная рекурсия с базовым случаем
fun factorial(n: Int): Long {
if (n <= 1) return 1 // Базовый случай - выход из рекурсии
return n * factorial(n - 1)
}
Жизненный цикл переменных в стеке
fun example() {
val x = 10 // x добавлена в стек
if (true) {
val y = 20 // y добавлена в стек frame if
} // y удалена из стека (выход из области видимости)
// println(y) // Ошибка компиляции: y не существует
println(x) // x ещё в стеке
} // x удалена из стека, функция завершена
Многопоточность и стек
Каждый поток имеет свой стек, что позволяет безопасно использовать локальные переменные в многопоточном коде:
fun main() {
// Поток 1 имеет свой стек
thread {
val threadLocal = 10 // Локально для потока 1
Thread.sleep(1000)
println(threadLocal) // Безопасно, не конфликтует с потоком 2
}
// Поток 2 имеет свой стек
thread {
val threadLocal = 20 // Локально для потока 2
println(threadLocal) // Независимо от потока 1
}
}
Best Practices
- Избегайте глубокую рекурсию — может привести к StackOverflowError
- Используйте итерацию вместо рекурсии для больших данных:
// ❌ Рекурсия для больших n
fun sum(n: Int): Long {
if (n <= 0) return 0
return n + sum(n - 1) // Может переполнить стек
}
// ✅ Итерация безопаснее
fun sum(n: Int): Long {
var total = 0L
for (i in 1..n) total += i
return total
}
- Помните о размере стека — для потоков часто задают меньший размер
- Не сохраняйте большие объекты в стеке — они должны быть в heap
Понимание стека критично для оптимизации производительности и избежания ошибок памяти в Android приложениях.