Сборка мусора в Go
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 23 из 107.
В отличие от таких языков, как C или C++, Go управляет памятью автоматически с помощью сборки мусора (GC). Вам не нужно вручную освобождать память, когда вы закончили с ней работать. Среда выполнения Go отслеживает, какая память все еще используется, и освобождает остальную.
Когда вы выделяете память с помощью new(), make() или путем создания переменных, сборщик мусора Go отслеживает эти аллокации. Как только значение становится недоступным для любой части вашей программы, GC автоматически освобождает эту память.
func createPerson() *Person {
p := &Person{Name: "Alice", Age: 30}
return p // p попадает в кучу, GC будет управлять им
}
func main() {
person := createPerson()
fmt.Println(person.Name)
// Когда person больше не используется, GC освобождает память
}В этом примере структура Person выделяется в куче, так как она возвращается из функции. Компилятор Go выполняет анализ побега (escape analysis), чтобы определить, может ли переменная остаться в стеке или она должна быть перемещена в кучу. Сборщик мусора управляет только памятью в куче.
Это автоматическое управление памятью означает, что вы можете сосредоточиться на создании своего приложения, не беспокоясь об утечках памяти из-за забытого освобождения ресурсов. Однако понимание того, что существует GC, помогает вам писать более эффективный код, минимизируя ненужные аллокации, когда производительность имеет значение.
Задание
ЛегкоДавайте создадим менеджер сессий, который демонстрирует, как сборщик мусора Go управляет памятью для объектов, попадающих в кучу (heap). Вы будете создавать сессии, которые выделяются динамически и возвращаются из функций, позволяя GC управлять их жизненным циклом.
Вы организуете свой код в двух файлах:
session.go: Определите структуруSessionс полямиID(string),Username(string) иData(срез строк). Создайте функциюNewSession, которая принимает ID и имя пользователя, выделяет новую структуру Session в куче (возвращая указатель) и инициализирует срез Data как пустой. Также добавьте методAddDataс получателем-указателем (pointer receiver), который добавляет строку в срез Data сессии, и методSummary, который возвращает отформатированную строку с деталями сессии.main.go: Считайте информацию о сессии из входных данных, создайте сессию с помощью вашей функции-конструктора, добавьте в нее несколько записей данных и выведите сводку по сессии. Поскольку сессия создается внутри функции и возвращается в виде указателя, она «убегает» (escapes) в кучу, где ею будет управлять сборщик мусора.
Будут предоставлены следующие входные данные:
- Строка 1: Session ID
- Строка 2: Username
- Строка 3: Первая запись данных для добавления
- Строка 4: Вторая запись данных для добавления
Ваш метод Summary должен возвращать строку в следующем формате:
Session [ID] for user [Username]
Data entries: [count]
- [entry1]
- [entry2]Например, для входных данных sess-001, alice, login и view_dashboard ваш вывод должен быть таким:
Session sess-001 for user alice
Data entries: 2
- login
- view_dashboardКлючевой момент здесь заключается в том, что ваша функция NewSession создает Session и возвращает указатель на нее. Это заставляет Session «убегать» в кучу, а не оставаться в стеке, что делает ее объектом для сборки мусора, как только на нее не останется ссылок. Вашему коду не нужно вручную освобождать эту память — Go делает это автоматически.
Шпаргалка
Go использует сборку мусора (GC) для автоматического управления памятью. Вам не нужно вручную освобождать память.
Сборщик мусора отслеживает выделение памяти и освобождает ту память, которая больше не доступна вашей программе.
func createPerson() *Person {
p := &Person{Name: "Alice", Age: 30}
return p // p попадает в кучу, GC будет управлять им
}
func main() {
person := createPerson()
fmt.Println(person.Name)
// Когда person больше не используется, GC освобождает память
}Компилятор Go выполняет анализ побега (escape analysis), чтобы определить, остается ли переменная в стеке или перемещается в кучу. Переменные, которые попадают в кучу (например, возвращаемые указатели), управляются сборщиком мусора.
GC управляет только памятью в куче, а не в стеке. Память в стеке освобождается автоматически при возврате функции.
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Чтение ID сессии
sessionID, _ := reader.ReadString('\n')
sessionID = sessionID[:len(sessionID)-1]
// Чтение имени пользователя
username, _ := reader.ReadString('\n')
username = username[:len(username)-1]
// Чтение первой записи данных
data1, _ := reader.ReadString('\n')
data1 = data1[:len(data1)-1]
// Чтение второй записи данных
data2, _ := reader.ReadString('\n')
data2 = data2[:len(data2)-1]
// TODO: Создайте новую сессию, используя функцию NewSession
// Сессия попадет в кучу (escape to the heap), так как мы возвращаем указатель
// TODO: Добавьте записи данных в сессию, используя метод AddData
// TODO: Выведите сводку сессии, используя метод Summary
fmt.Println("")
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker Pool