From 7717c209d322f8df88a267c541e5dab3ad28c307 Mon Sep 17 00:00:00 2001 From: korepanov Date: Fri, 15 May 2026 12:24:43 +0300 Subject: [PATCH 01/24] [+][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 golang/golang_chapter_0200/text.md diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md new file mode 100644 index 00000000..83c1d702 --- /dev/null +++ b/golang/golang_chapter_0200/text.md @@ -0,0 +1,4 @@ +# Глава 20. Сборщик мусора + +Как правило, разработчику на языке Go не приходится задумываться о том, где и как выделяется память. Однако на практике выделение памяти, ее освобождение и повторное использование — непростой процесс. В конечном итоге нужно задействовать физическую память компьютера. Физическая память — ограниченный ресурс, поэтому так важно распоряжаться им разумно. В Go каждое приложение содержит **библиотеку времени выполнения**. Эта библиотека включает в себя инструменты по управлению памятью, в том числе **сборщик мусора**. Сборщик мусора освобождает память, которая больше не нужна, и позволяет использовать ее повторно. + From 063abb848ed4e0555b419a087aa3942188112a3e Mon Sep 17 00:00:00 2001 From: korepanov Date: Fri, 15 May 2026 14:41:36 +0300 Subject: [PATCH 02/24] [+][golang_chapter_200] text.md --- golang/golang_chapter_0200/text.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 83c1d702..84554b06 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -2,3 +2,15 @@ Как правило, разработчику на языке Go не приходится задумываться о том, где и как выделяется память. Однако на практике выделение памяти, ее освобождение и повторное использование — непростой процесс. В конечном итоге нужно задействовать физическую память компьютера. Физическая память — ограниченный ресурс, поэтому так важно распоряжаться им разумно. В Go каждое приложение содержит **библиотеку времени выполнения**. Эта библиотека включает в себя инструменты по управлению памятью, в том числе **сборщик мусора**. Сборщик мусора освобождает память, которая больше не нужна, и позволяет использовать ее повторно. +Начиная с версии Go 1.25, появился новый сборщик мусора — **Green Tea**. Сначала он был экспериментальным, но с версии Go 1.26 стал сборщиком мусора по умолчанию. Важно понимать, как работают оба варианта, ведь многие программы уже написаны на старом Go. Мы рассмотрим сначала старый сборщик мусора, а затем поговорим про новый. + +## Старый сборщик мусора +### Какую память очищает сборщик мусора + +Прежде чем рассматривать сборщик мусора, поговорим о памяти, которую сборщик мусора **не** очищает. Сборщик мусора не очищает значения локальных переменных, которые не являются указателями. Такие переменные хранятся в на **стеке** горутины. Компилятор Go сам может определить, когда их необходимо очистить. Для этого он генерирует соответствующие машинные инструкции. + +Есть переменные, время жизни которых компилятор определить не способен. Например, возвращаемые из функции переменные. Они живут дольше функции: неизвестно, когда их нужно уничтожить. Существуют большие значения, которые не помещаются в стек. Например, объемный массив. Иногда мы и вовсе не знаем о размере значения. Например, для базового массива среза, начальный размер которого определяется переменной, а не константой. Память в таких ситуациях выделяется динамически. Все эти значения помещаются в **кучу**. Именно с ними работает сборщик мусора. + +## Новый сборщик мусора Green Tea + +## Резюме \ No newline at end of file From 272548028520eb2dace8adc54db47dfdffd3e6f8 Mon Sep 17 00:00:00 2001 From: korepanov Date: Fri, 15 May 2026 16:42:53 +0300 Subject: [PATCH 03/24] [+][golang_chapter_0200] --- golang/golang_chapter_0200/text.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 84554b06..73a337b1 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -11,6 +11,24 @@ Есть переменные, время жизни которых компилятор определить не способен. Например, возвращаемые из функции переменные. Они живут дольше функции: неизвестно, когда их нужно уничтожить. Существуют большие значения, которые не помещаются в стек. Например, объемный массив. Иногда мы и вовсе не знаем о размере значения. Например, для базового массива среза, начальный размер которого определяется переменной, а не константой. Память в таких ситуациях выделяется динамически. Все эти значения помещаются в **кучу**. Именно с ними работает сборщик мусора. +### Трассировка сборщика мусора + +Сборщик мусора в Go относится к **трассирующим сборщикам**. Он определяет используемые объекты, следуя по указателям. **Объект** в нашей терминологии — это динамически выделенный участок памяти, который содержит одно или несколько значений. **Указатель** — это адрес любого значения внутри объекта. Указатель — это, например, переменные типа `*int` или `*float`. Также это строки, срезы, каналы, хеш-таблицы и значения интерфейсов. Вместе объекты и указатели образуют граф. Сборщик мусора проходится по этому графу и отмечает те объекты, которые программа использует. Процесс обхода графа называется **сканированием**. После сканирования сборщик мусора проходит по всей памяти в куче и делает доступной всю не помеченную память. + +### Цикл сборки мусора + +Как мы уже поняли, сборщик мусора работает может работать в режиме пометки и режиме очистки памяти. Процессы пометки и очистки не могут быть выполнены параллельно. Пока не поймем, что на данный объект не ссылается ни один указатель, мы не можем очистить память. Кроме того, сборщик мусора бывает вообще не активен, когда нет никакой работы, связанной со сборкой мусора. Таким образом, сборщик мусора находится в одном из трех состояний: пометка, очистка и выключение. Непрерывный переход сборщиком мусора из одного состояние в другое называется **циклом сборки мусора**. + +### Затраты на сборку мусора + +В этом разделе будет показано, как оценить стоимость на сборку мусора. + +Сборщик мусора использует только два ресурса: физическую память и процессорное время. + +Затраты на память состоят из активной памяти кучи, новой памяти кучи, выделенной до фазы пометки, и метаданных. Метаданные довольно малы по сравнению с остальными затратами на память. Активная память кучи — это память, которая была помечена сборщиком мусора в предыдущем цикле. Новая память кучи — это память, выделенная в текущем цикле, которая может стать активной или нет к концу цикла. Объем активной памяти является свойством программы, а не чем-то, что сборщик мусора может контролировать. + +Стоимость памяти для цикла `N` сборщика мусора определяется количеством активной памяти кучи из цикла `N-1` и новой памяти. + ## Новый сборщик мусора Green Tea ## Резюме \ No newline at end of file From 3a155bea0bf5b14c7bacb8a47faa6b744dbea414 Mon Sep 17 00:00:00 2001 From: korepanov Date: Mon, 18 May 2026 14:01:58 +0300 Subject: [PATCH 04/24] [*][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 73a337b1..99469d58 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -2,7 +2,7 @@ Как правило, разработчику на языке Go не приходится задумываться о том, где и как выделяется память. Однако на практике выделение памяти, ее освобождение и повторное использование — непростой процесс. В конечном итоге нужно задействовать физическую память компьютера. Физическая память — ограниченный ресурс, поэтому так важно распоряжаться им разумно. В Go каждое приложение содержит **библиотеку времени выполнения**. Эта библиотека включает в себя инструменты по управлению памятью, в том числе **сборщик мусора**. Сборщик мусора освобождает память, которая больше не нужна, и позволяет использовать ее повторно. -Начиная с версии Go 1.25, появился новый сборщик мусора — **Green Tea**. Сначала он был экспериментальным, но с версии Go 1.26 стал сборщиком мусора по умолчанию. Важно понимать, как работают оба варианта, ведь многие программы уже написаны на старом Go. Мы рассмотрим сначала старый сборщик мусора, а затем поговорим про новый. +Начиная с версии Go 1.25, появился новый сборщик мусора — **Green Tea**. Сначала он был экспериментальным, но с версии Go 1.26 стал сборщиком мусора по умолчанию. Важно понимать, как работают оба сборщика, ведь многие программы уже написаны на старом Go. Мы рассмотрим сначала старый сборщик мусора, а затем поговорим про новый. ## Старый сборщик мусора ### Какую память очищает сборщик мусора @@ -17,17 +17,7 @@ ### Цикл сборки мусора -Как мы уже поняли, сборщик мусора работает может работать в режиме пометки и режиме очистки памяти. Процессы пометки и очистки не могут быть выполнены параллельно. Пока не поймем, что на данный объект не ссылается ни один указатель, мы не можем очистить память. Кроме того, сборщик мусора бывает вообще не активен, когда нет никакой работы, связанной со сборкой мусора. Таким образом, сборщик мусора находится в одном из трех состояний: пометка, очистка и выключение. Непрерывный переход сборщиком мусора из одного состояние в другое называется **циклом сборки мусора**. - -### Затраты на сборку мусора - -В этом разделе будет показано, как оценить стоимость на сборку мусора. - -Сборщик мусора использует только два ресурса: физическую память и процессорное время. - -Затраты на память состоят из активной памяти кучи, новой памяти кучи, выделенной до фазы пометки, и метаданных. Метаданные довольно малы по сравнению с остальными затратами на память. Активная память кучи — это память, которая была помечена сборщиком мусора в предыдущем цикле. Новая память кучи — это память, выделенная в текущем цикле, которая может стать активной или нет к концу цикла. Объем активной памяти является свойством программы, а не чем-то, что сборщик мусора может контролировать. - -Стоимость памяти для цикла `N` сборщика мусора определяется количеством активной памяти кучи из цикла `N-1` и новой памяти. +Как мы уже поняли, сборщик мусора может работать в режиме пометки и режиме очистки памяти. Процессы пометки и очистки не могут быть выполнены параллельно. Пока не поймем, что на данный объект не ссылается ни один указатель, мы не можем очистить память. Кроме того, сборщик мусора бывает вообще не активен, когда для него нет никакой работы. Таким образом, сборщик мусора находится в одном из трех состояний: пометка, очистка и выключение. Непрерывный переход из одного состояние в другое называется **циклом сборки мусора**. ## Новый сборщик мусора Green Tea From eeaf817a8ef7996ae60c952463a1fa0b42fecd91 Mon Sep 17 00:00:00 2001 From: korepanov Date: Mon, 18 May 2026 16:00:52 +0300 Subject: [PATCH 05/24] [+][golang_chapter_0200]: text.md --- golang/golang_chapter_0200/text.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 99469d58..34b2b2d5 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -19,6 +19,28 @@ Как мы уже поняли, сборщик мусора может работать в режиме пометки и режиме очистки памяти. Процессы пометки и очистки не могут быть выполнены параллельно. Пока не поймем, что на данный объект не ссылается ни один указатель, мы не можем очистить память. Кроме того, сборщик мусора бывает вообще не активен, когда для него нет никакой работы. Таким образом, сборщик мусора находится в одном из трех состояний: пометка, очистка и выключение. Непрерывный переход из одного состояние в другое называется **циклом сборки мусора**. +### Затраты на сборку мусора + +В этом разделе будет показано, как оценить стоимость на сборку мусора. + +Сборщик мусора использует только два ресурса: физическую память и процессорное время. + +Затраты на память состоят из активной памяти кучи, новой памяти кучи, выделенной до фазы пометки, и метаданных. Метаданные довольно малы по сравнению с остальными затратами на память. Активная память кучи — это память, которая была помечена сборщиком мусора в предыдущем цикле. Новая память кучи — это память, выделенная в текущем цикле, которая может стать активной или нет к концу цикла. Объем активной памяти является свойством программы, а не чем-то, что сборщик мусора может контролировать. + +Стоимость памяти для цикла `N` сборщика мусора определяется количеством активной памяти кучи из цикла `N-1` и новой памяти. + +Как мы уже сказали, помимо памяти, сборщик мусора затрачивает процессорное время. Время, затраченное за цикл `N`, определяется двумя составляющими. Первая — фиксированное время за цикл. Вторая — произведение среднего времени работы процессора за байт на объем активной памяти кучи в цикле `N`. + +Фиксированная время за цикл — это время на те действия, которые выполняются одинаковое количество раз в каждом цикле. Например, инициализации структур для следующего цикла сборки мусора. Это время сравнительно невелико. + +Большая часть затрат процессора приходится на пометку и сканирование. Средняя стоимость пометки и сканирования зависит от поведения программы. Например, чем больше указателей, тем больше работы, поскольку необходимо посетить все указатели в программе. Связные списки и деревья также сложно обрабатывать, они увеличивают среднюю стоимость байта. + +Затраченное процессорное время зависит от общего количества циклов сборщика мусора за заданный промежуток времени. Кроме того, чем чаще запускается сборщик мусора, тем меньше памяти он тратит. Однако тем больше мы расходуем процессорного времени. Как найти компромисс между тем и другим? За то, как часто запускать сборщик мусора, отвечает параметр **GOGC**. + +## GOGC + +GOGC определяет компромисс между производительностью сборщика мусора и памятью, которую он использует. Он определяет целевой размер кучи после каждого цикла сборки мусора. + ## Новый сборщик мусора Green Tea ## Резюме \ No newline at end of file From 6c0969f9e939f8973dbfa65dd34001e5d51d3b16 Mon Sep 17 00:00:00 2001 From: korepanov Date: Tue, 19 May 2026 15:36:34 +0300 Subject: [PATCH 06/24] [+][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 34b2b2d5..a65aec44 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -39,7 +39,7 @@ ## GOGC -GOGC определяет компромисс между производительностью сборщика мусора и памятью, которую он использует. Он определяет целевой размер кучи после каждого цикла сборки мусора. +GOGC определяет компромисс между производительностью сборщика мусора и памятью, которую он использует. Он определяет целевой размер кучи после каждого цикла сборки мусора. Сборщик мусора должен завершить цикл сборки мусора до того, как общий размер кучи превысит целевой. Общий размер кучи складывается из ## Новый сборщик мусора Green Tea From de23d74b67edb46af19052102b92b862c93448f1 Mon Sep 17 00:00:00 2001 From: korepanov Date: Tue, 26 May 2026 14:31:31 +0300 Subject: [PATCH 07/24] [!][golang_chapter_0200] text.md: typo --- golang/golang_chapter_0200/text.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index a65aec44..d0f8e439 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -7,7 +7,7 @@ ## Старый сборщик мусора ### Какую память очищает сборщик мусора -Прежде чем рассматривать сборщик мусора, поговорим о памяти, которую сборщик мусора **не** очищает. Сборщик мусора не очищает значения локальных переменных, которые не являются указателями. Такие переменные хранятся в на **стеке** горутины. Компилятор Go сам может определить, когда их необходимо очистить. Для этого он генерирует соответствующие машинные инструкции. +Прежде чем рассматривать сборщик мусора, поговорим о памяти, которую сборщик мусора **не** очищает. Сборщик мусора не очищает значения локальных переменных, которые не являются указателями. Такие переменные хранятся на **стеке** горутины. Компилятор Go сам может определить, когда их необходимо очистить. Для этого он генерирует соответствующие машинные инструкции. Есть переменные, время жизни которых компилятор определить не способен. Например, возвращаемые из функции переменные. Они живут дольше функции: неизвестно, когда их нужно уничтожить. Существуют большие значения, которые не помещаются в стек. Например, объемный массив. Иногда мы и вовсе не знаем о размере значения. Например, для базового массива среза, начальный размер которого определяется переменной, а не константой. Память в таких ситуациях выделяется динамически. Все эти значения помещаются в **кучу**. Именно с ними работает сборщик мусора. From b963e4b866fb6eae4c914119143deb48679236e5 Mon Sep 17 00:00:00 2001 From: korepanov Date: Tue, 26 May 2026 15:37:10 +0300 Subject: [PATCH 08/24] [+][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 36 +++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index d0f8e439..75827aa0 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -39,7 +39,41 @@ ## GOGC -GOGC определяет компромисс между производительностью сборщика мусора и памятью, которую он использует. Он определяет целевой размер кучи после каждого цикла сборки мусора. Сборщик мусора должен завершить цикл сборки мусора до того, как общий размер кучи превысит целевой. Общий размер кучи складывается из +GOGC определяет компромисс между производительностью сборщика мусора и памятью, которую он использует. Он определяет целевой размер кучи после каждого цикла сборки мусора. Сборщик мусора должен завершить цикл сборки мусора до того, как общий размер кучи превысит целевой. Общий размер кучи складывается из размера активной и новой памяти кучи за предыдущий цикл. Чтобы понять, как рассчитывается целевой размер кучи, нам нужно сначала ввести понятие **GC roots**. + +GC Roots — это набор специальных, всегда доступных «исходных точек» в памяти программы. С них сборщик мусора начинает свой обход. К ним относятся глобальные переменные и стеки вызовов горутин. + +Целевой размер кучи определяется формулой: + +``` +Ц = А + (А + К) * Г / 100 +``` + +Здесь +* Ц — целевой размер кучи, +* А — размер активный кучи, +* К — размер GC Roots, +* Г — параметр GOGC. + +Рассмотрим пример. Пусть в программе на Go размер активной кучи равен 8 Мб, размер стеков горутин — 1 Мб и размер памяти, которая доступна по глобальным указателям, — 1 Мб. Допустим, GOGC равен 100. Тогда целевой размер кучи: + +``` +Ц = 8 + (8 + (1 + 1)) * 100 / 100 = 18 Мб +``` + +Когда GOGC равен 50: + +``` +Ц = 8 + (8 + (1 + 1)) * 50 / 100 = 13 Мб +``` + +Когда GOGC равен 200: + +``` +Ц = 8 + (8 + (1 + 1)) * 200 / 100 = 28 Мб +``` + +Чем больше GOGC, тем больше объем целевой кучи. ## Новый сборщик мусора Green Tea From bb3b676e7fd744200a7dc22449142e533c585246 Mon Sep 17 00:00:00 2001 From: korepanov Date: Tue, 26 May 2026 17:41:21 +0300 Subject: [PATCH 09/24] [+][golang_chapter_0200]: text.md --- golang/golang_chapter_0200/text.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 75827aa0..3676378c 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -50,10 +50,10 @@ GC Roots — это набор специальных, всегда доступ ``` Здесь -* Ц — целевой размер кучи, -* А — размер активный кучи, -* К — размер GC Roots, -* Г — параметр GOGC. +* `Ц` — целевой размер кучи, +* `А` — размер активный кучи, +* `К` — размер GC Roots, +* `Г` — параметр GOGC. Рассмотрим пример. Пусть в программе на Go размер активной кучи равен 8 Мб, размер стеков горутин — 1 Мб и размер памяти, которая доступна по глобальным указателям, — 1 Мб. Допустим, GOGC равен 100. Тогда целевой размер кучи: @@ -73,7 +73,19 @@ GC Roots — это набор специальных, всегда доступ Ц = 8 + (8 + (1 + 1)) * 200 / 100 = 28 Мб ``` -Чем больше GOGC, тем больше объем целевой кучи. +Чем больше GOGC, тем больше целевой размер кучи. В свою очередь чем больше целевой размер кучи, тем реже нужно запускаться сборщику мусора. Тем больше памяти и меньше процессорного времени он потребляет. И наоборот. Говоря проще, чем больше значение GOGC, тем больше памяти и меньше процессорного времени расходуется. Чем меньше GOGC, тем меньше памяти и больше процессорного времени будет потрачено. + +### Диапазон значений GOGC + +GOGC может принимать значения в следующем диапазоне. + +* значение `off` — установить значение GOGC бесконечным. Эта опция часто используется для полного отключения сборщика мусора. Однако одной ее для этого недостаточно. Как отключить сборщик мусора полностью — будет рассмотрено далее. +* [`min.Int64; 0)` — аналогично значению `off`. +* `0` — сборщик мусора включается почти постоянно. +* `[1; 100)` — более частая сборка, экономнее расходует память, но затрачивает больше процессорного времени. +* `100` — стандартный режим, хорошо подходит для большинства программ. +* `[101; max.Int64]` — более редкая сборка, выше производительность, но растет и потребление памяти. + ## Новый сборщик мусора Green Tea From f9333c1257b15d103be16a7bef2eba8e6c291ffd Mon Sep 17 00:00:00 2001 From: korepanov Date: Wed, 27 May 2026 15:42:23 +0300 Subject: [PATCH 10/24] [+][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 38 ++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 3676378c..d7a65498 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -75,18 +75,48 @@ GC Roots — это набор специальных, всегда доступ Чем больше GOGC, тем больше целевой размер кучи. В свою очередь чем больше целевой размер кучи, тем реже нужно запускаться сборщику мусора. Тем больше памяти и меньше процессорного времени он потребляет. И наоборот. Говоря проще, чем больше значение GOGC, тем больше памяти и меньше процессорного времени расходуется. Чем меньше GOGC, тем меньше памяти и больше процессорного времени будет потрачено. -### Диапазон значений GOGC +Если пренебречь размером GC Roots, то можно сказать, что GOGC — это целевое значение для роста активной кучи, выраженное в процентах. Другими словами, то, насколько куча может временно разрастись за счет мусора. -GOGC может принимать значения в следующем диапазоне. +### Значения GOGC + +Вот какие значения принимает GOGC. * значение `off` — установить значение GOGC бесконечным. Эта опция часто используется для полного отключения сборщика мусора. Однако одной ее для этого недостаточно. Как отключить сборщик мусора полностью — будет рассмотрено далее. -* [`min.Int64; 0)` — аналогично значению `off`. +* `-1` — аналогично значению `off`. * `0` — сборщик мусора включается почти постоянно. * `[1; 100)` — более частая сборка, экономнее расходует память, но затрачивает больше процессорного времени. * `100` — стандартный режим, хорошо подходит для большинства программ. -* `[101; max.Int64]` — более редкая сборка, выше производительность, но растет и потребление памяти. +* `>100` — более редкая сборка, выше производительность, но растет и потребление памяти. + +Максимальное значение GOGC зависит от конкретных реализаций и платформы, на которой выполняется программа. На практике гигантские значения GOGC не имеют смысла. Для этого есть значения `off` и `-1`. + +### Установка значения GOGC + +Есть два способа установить значение GOGC: + +1. Во время выполнения программы, через функцию `debug.SetGCPercent`: + + ```go + package main + + import "runtime/debug" + + func main() { + debug.SetGCPercent(50) + } + ``` + + Функция `debug.SetGCPercent` при установке нового значения возвращает предыдущее. Если программа запущена с настройками по умолчанию, то она вернет значение `100`. + +2. Через переменную окружения. Например, в Linux со значением `GOGC=off`: + + ```bash + GOGC=off go run cmd/main.go + ``` + +## Полное отключение сборщика мусора ## Новый сборщик мусора Green Tea ## Резюме \ No newline at end of file From f188d2f313ff76a1108508cf75963ff666f6839b Mon Sep 17 00:00:00 2001 From: korepanov Date: Wed, 27 May 2026 17:42:43 +0300 Subject: [PATCH 11/24] [+][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 214 +++++++++++++++++++++++++++-- 1 file changed, 199 insertions(+), 15 deletions(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index d7a65498..beab7337 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -2,7 +2,7 @@ Как правило, разработчику на языке Go не приходится задумываться о том, где и как выделяется память. Однако на практике выделение памяти, ее освобождение и повторное использование — непростой процесс. В конечном итоге нужно задействовать физическую память компьютера. Физическая память — ограниченный ресурс, поэтому так важно распоряжаться им разумно. В Go каждое приложение содержит **библиотеку времени выполнения**. Эта библиотека включает в себя инструменты по управлению памятью, в том числе **сборщик мусора**. Сборщик мусора освобождает память, которая больше не нужна, и позволяет использовать ее повторно. -Начиная с версии Go 1.25, появился новый сборщик мусора — **Green Tea**. Сначала он был экспериментальным, но с версии Go 1.26 стал сборщиком мусора по умолчанию. Важно понимать, как работают оба сборщика, ведь многие программы уже написаны на старом Go. Мы рассмотрим сначала старый сборщик мусора, а затем поговорим про новый. +Начиная с версии Go 1.25, появился новый сборщик мусора — **Green Tea**. Сначала он был экспериментальным, но с версии Go 1.26 стал сборщиком мусора по умолчанию. Green Tea — это результат некоторых улучшений старого сборщика мусора. Мы сначала поговорим про старый сборщик мусора, а потом рассмотрим, какие изменения появились в новом. Важно понимать, что все, о чем мы будем говорить про старый сборщик мусора, актуально и для нового. ## Старый сборщик мусора ### Какую память очищает сборщик мусора @@ -37,9 +37,9 @@ Затраченное процессорное время зависит от общего количества циклов сборщика мусора за заданный промежуток времени. Кроме того, чем чаще запускается сборщик мусора, тем меньше памяти он тратит. Однако тем больше мы расходуем процессорного времени. Как найти компромисс между тем и другим? За то, как часто запускать сборщик мусора, отвечает параметр **GOGC**. -## GOGC +### GOGC -GOGC определяет компромисс между производительностью сборщика мусора и памятью, которую он использует. Он определяет целевой размер кучи после каждого цикла сборки мусора. Сборщик мусора должен завершить цикл сборки мусора до того, как общий размер кучи превысит целевой. Общий размер кучи складывается из размера активной и новой памяти кучи за предыдущий цикл. Чтобы понять, как рассчитывается целевой размер кучи, нам нужно сначала ввести понятие **GC roots**. +`GOGC` определяет компромисс между производительностью сборщика мусора и памятью, которую он использует. Он определяет целевой размер кучи после каждого цикла сборки мусора. Сборщик мусора должен завершить цикл сборки мусора до того, как общий размер кучи превысит целевой. Общий размер кучи складывается из размера активной и новой памяти кучи за предыдущий цикл. Чтобы понять, как рассчитывается целевой размер кучи, нам нужно сначала ввести понятие **GC roots**. GC Roots — это набор специальных, всегда доступных «исходных точек» в памяти программы. С них сборщик мусора начинает свой обход. К ним относятся глобальные переменные и стеки вызовов горутин. @@ -53,46 +53,46 @@ GC Roots — это набор специальных, всегда доступ * `Ц` — целевой размер кучи, * `А` — размер активный кучи, * `К` — размер GC Roots, -* `Г` — параметр GOGC. +* `Г` — параметр `GOGC`. -Рассмотрим пример. Пусть в программе на Go размер активной кучи равен 8 Мб, размер стеков горутин — 1 Мб и размер памяти, которая доступна по глобальным указателям, — 1 Мб. Допустим, GOGC равен 100. Тогда целевой размер кучи: +Рассмотрим пример. Пусть в программе на Go размер активной кучи равен 8 Мб, размер стеков горутин — 1 Мб и размер памяти, которая доступна по глобальным указателям, — 1 Мб. Допустим, `GOGC` равен 100. Тогда целевой размер кучи: ``` Ц = 8 + (8 + (1 + 1)) * 100 / 100 = 18 Мб ``` -Когда GOGC равен 50: +Когда `GOGC` равен 50: ``` Ц = 8 + (8 + (1 + 1)) * 50 / 100 = 13 Мб ``` -Когда GOGC равен 200: +Когда `GOGC` равен 200: ``` Ц = 8 + (8 + (1 + 1)) * 200 / 100 = 28 Мб ``` -Чем больше GOGC, тем больше целевой размер кучи. В свою очередь чем больше целевой размер кучи, тем реже нужно запускаться сборщику мусора. Тем больше памяти и меньше процессорного времени он потребляет. И наоборот. Говоря проще, чем больше значение GOGC, тем больше памяти и меньше процессорного времени расходуется. Чем меньше GOGC, тем меньше памяти и больше процессорного времени будет потрачено. +Чем больше `GOGC`, тем больше целевой размер кучи. В свою очередь чем больше целевой размер кучи, тем реже нужно запускаться сборщику мусора. Тем больше памяти и меньше процессорного времени он потребляет. И наоборот. Говоря проще, чем больше значение `GOGC`, тем больше памяти и меньше процессорного времени расходуется. Чем меньше `GOGC`, тем меньше памяти и больше процессорного времени будет потрачено. -Если пренебречь размером GC Roots, то можно сказать, что GOGC — это целевое значение для роста активной кучи, выраженное в процентах. Другими словами, то, насколько куча может временно разрастись за счет мусора. +Если пренебречь размером GC Roots, то можно сказать, что `GOGC` — это целевое значение для роста активной кучи, выраженное в процентах. Другими словами, то, насколько куча может временно разрастись за счет мусора. ### Значения GOGC -Вот какие значения принимает GOGC. +Вот какие значения принимает `GOGC`. -* значение `off` — установить значение GOGC бесконечным. Эта опция часто используется для полного отключения сборщика мусора. Однако одной ее для этого недостаточно. Как отключить сборщик мусора полностью — будет рассмотрено далее. +* значение `off` — установить значение `GOGC` бесконечным. Эта опция часто используется для полного отключения сборщика мусора. Однако одной ее для этого недостаточно. Как отключить сборщик мусора полностью — будет рассмотрено далее. * `-1` — аналогично значению `off`. * `0` — сборщик мусора включается почти постоянно. * `[1; 100)` — более частая сборка, экономнее расходует память, но затрачивает больше процессорного времени. * `100` — стандартный режим, хорошо подходит для большинства программ. * `>100` — более редкая сборка, выше производительность, но растет и потребление памяти. -Максимальное значение GOGC зависит от конкретных реализаций и платформы, на которой выполняется программа. На практике гигантские значения GOGC не имеют смысла. Для этого есть значения `off` и `-1`. +Максимальное значение `GOGC` зависит от конкретных реализаций и платформы, на которой выполняется программа. На практике гигантские значения `GOGC` не имеют смысла. Для этого есть `off` и `-1`. ### Установка значения GOGC -Есть два способа установить значение GOGC: +Есть два способа установить значение `GOGC`: 1. Во время выполнения программы, через функцию `debug.SetGCPercent`: @@ -114,9 +114,193 @@ GC Roots — это набор специальных, всегда доступ GOGC=off go run cmd/main.go ``` +Рассмотрим пример. Запустим сервер, который отдает некоторые данные. Функция `generateResponse` генерирует эти данные. Функция `runLoadTest` запускает нагрузочный тест со стороны клиента и получает данные с сервера. Чем больше значение `GOGC`, тем больше запросов в единицу времени успевает обработать сервер. Однако тем больше памяти для этого требуется. Обратите внимание, что и количество циклов сборщика уменьшается по мере увеличения `GOGC`. + +```go {.example_for_playground} +package main + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "runtime" + "runtime/debug" + "sync" + "sync/atomic" + "time" +) + +// Время на нагрузочный тест +const timeToLoadMs = 300 + +// Ответ сервера. Справа указано, как будет называться +// поле в структуре json. +type response struct { + ID int `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Tags []string `json:"tags"` + Metadata map[string]string `json:"metadata"` + Items []item `json:"items"` +} + +type item struct { + Index int `json:"index"` + Value float64 `json:"value"` + Active bool `json:"active"` + Data []byte `json:"data"` +} + +func generateResponse(id int) *response { + const itemsNumber = 100 + items := make([]item, itemsNumber) + for i := range itemsNumber { + items[i] = item{ + Index: i, Value: float64(i) * 1.5, + Active: i%2 == 0, + Data: make([]byte, 1024), + } + } + const metadataNumber = 50 + metadata := make(map[string]string, metadataNumber) + for i := range metadataNumber { + metadata[fmt.Sprintf("key_%d", i)] = fmt.Sprintf("%d", i) + } + const tagsNumber = 10 + tags := make([]string, tagsNumber) + for i := range tagsNumber { + tags[i] = fmt.Sprintf("tag_%d_with_description", i) + } + return &response{ + ID: id, + Name: fmt.Sprintf("Object%d", id), + Description: fmt.Sprintf("Description%d", id), + Tags: tags, Metadata: metadata, + Items: items, + } +} + +// Нагрузочный тест, клиент +func runLoadTest(url string) { + client := &http.Client{Timeout: timeToLoadMs * time.Millisecond} + stop := time.After(timeToLoadMs * time.Millisecond) + for { + select { + case <-stop: + return + default: + resp, err := client.Get(url) + exitOnErr(err) + resp.Body.Close() + } + } +} + +func main() { + testValues := []struct { + gogc int + name string + }{ + {50, "Memory-efficient, GOGC=50"}, + {100, "Standard GC, GOGC=100"}, + {150, "More memory usage, GOGC=150"}, + } + + for _, test := range testValues { + fmt.Printf("\nTest: %s\n", test.name) + debug.SetGCPercent(test.gogc) + // Количество запросов + var requestCount int64 + var peakHeapAlloc uint64 + var peakMutex sync.Mutex + apiHandler := func(w http.ResponseWriter, r *http.Request) { + var m runtime.MemStats + // Замеряем статистику по памяти + runtime.ReadMemStats(&m) + peakMutex.Lock() + peakHeapAlloc = max(peakHeapAlloc, m.HeapAlloc) + peakMutex.Unlock() + // Пакет atomic позволяет сделать операцию неделимой, + // это нужно для низкоуровневой синхронизации + // горутин без мьютексов. + // Это значит, что во время изменения переменной + // другая горутина не сможет вмешаться. + count := atomic.AddInt64(&requestCount, 1) + response := generateResponse(int(count)) + // Сериализуем в JSON + data, err := json.Marshal(response) + exitOnErr(err) + // Отправляем ответ + w.Header().Set("Content-Type", "application/json") + w.Write(data) + } + // Устанавливаем обработчик apiHandler для сервера + server := &http.Server{ + Addr: ":8080", Handler: http.HandlerFunc(apiHandler), + } + // Запускаем сервер в фоне + go server.ListenAndServe() + // Запускаем сборщик перед тестом + runtime.GC() + atomic.StoreInt64(&requestCount, 0) + var m1 runtime.MemStats + runtime.ReadMemStats(&m1) + fmt.Printf("Running the load for %d milliseconds...\n", timeToLoadMs) + loadStart := time.Now() + runLoadTest("http://localhost:8080") + loadElapsed := time.Since(loadStart) + var m2 runtime.MemStats + runtime.ReadMemStats(&m2) + fmt.Printf("\nStatistics after the test:\n") + fmt.Printf(" Total requests: %d\n", atomic.LoadInt64(&requestCount)) + fmt.Printf(" Requests/ms: %.1f\n", + float64(atomic.LoadInt64(&requestCount))/ + float64(loadElapsed.Milliseconds())) + fmt.Printf(" Peak memory: %.1f MB\n", float64(peakHeapAlloc)/1024/1024) + fmt.Printf(" Number of GC cycles: %d\n", m2.NumGC-m1.NumGC) + server.Close() + } +} + +func exitOnErr(err error) { + if err != nil { + log.Fatal(err) + } +} +``` +``` +Test: Memory-efficient, GOGC=50 +Running the load for 300 milliseconds... + +Statistics after the test: + Total requests: 525 + Requests/ms: 1.8 + Peak memory: 2.2 MB + Number of GC cycles: 417 + +Test: Standard GC, GOGC=100 +Running the load for 300 milliseconds... + +Statistics after the test: + Total requests: 710 + Requests/ms: 2.4 + Peak memory: 3.5 MB + Number of GC cycles: 184 + +Test: More memory usage, GOGC=150 +Running the load for 300 milliseconds... + +Statistics after the test: + Total requests: 798 + Requests/ms: 2.7 + Peak memory: 5.4 MB + Number of GC cycles: 75 +``` +### Ограничение по памяти MemoryLimit -## Полное отключение сборщика мусора +### Полное отключение сборщика мусора ## Новый сборщик мусора Green Tea -## Резюме \ No newline at end of file +## Резюме From 74e85baa1f3f04e462971cabbb74f1f147a3cad2 Mon Sep 17 00:00:00 2001 From: korepanov Date: Thu, 28 May 2026 12:24:02 +0300 Subject: [PATCH 12/24] [*][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index beab7337..9603c7d6 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -25,7 +25,7 @@ Сборщик мусора использует только два ресурса: физическую память и процессорное время. -Затраты на память состоят из активной памяти кучи, новой памяти кучи, выделенной до фазы пометки, и метаданных. Метаданные довольно малы по сравнению с остальными затратами на память. Активная память кучи — это память, которая была помечена сборщиком мусора в предыдущем цикле. Новая память кучи — это память, выделенная в текущем цикле, которая может стать активной или нет к концу цикла. Объем активной памяти является свойством программы, а не чем-то, что сборщик мусора может контролировать. +Затраты на память состоят из активной памяти кучи, новой памяти кучи и метаданных. Метаданные довольно малы по сравнению с остальными затратами на память. Активная память кучи — это память, которая была помечена сборщиком мусора в предыдущем цикле. Новая память кучи — это память, выделенная в текущем цикле, которая может стать активной или нет к концу цикла. Объем активной памяти является свойством программы, а не чем-то, что сборщик мусора может контролировать. Стоимость памяти для цикла `N` сборщика мусора определяется количеством активной памяти кучи из цикла `N-1` и новой памяти. @@ -88,7 +88,7 @@ GC Roots — это набор специальных, всегда доступ * `100` — стандартный режим, хорошо подходит для большинства программ. * `>100` — более редкая сборка, выше производительность, но растет и потребление памяти. -Максимальное значение `GOGC` зависит от конкретных реализаций и платформы, на которой выполняется программа. На практике гигантские значения `GOGC` не имеют смысла. Для этого есть `off` и `-1`. +Максимальное значение `GOGC` зависит от конкретных реализаций сборщика мусора и платформы, на которой выполняется программа. На практике гигантские значения `GOGC` не имеют смысла. Для этого есть `off` и `-1`. ### Установка значения GOGC From 04392beb9b367e2964c4f75a079e13df420df7c4 Mon Sep 17 00:00:00 2001 From: korepanov Date: Thu, 28 May 2026 14:11:05 +0300 Subject: [PATCH 13/24] [+][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 9603c7d6..c0ee2a64 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -300,6 +300,9 @@ Statistics after the test: ### Ограничение по памяти MemoryLimit +Хотя `GOGC` определяет компромисс между использованием памяти и процессорного времени, он не учитывает, что доступная память конечна. Рассмотрим пример. Допустим, произошел кратковременный рост размера кучи. Сборщик мусора оперирует общим размером кучи, пропорционально этому кратковременному значению. Памяти для этого пика может не хватить. Таким образом, `GOGC` необходимо настроить с учетом пикового размера кучи. Даже если в других циклах сборщика мусора для большого значения `GOGC` памяти хватает, мы не можем установить это значение. Хотя эти пики по памяти могут быть редкими, большое значение `GOGC` рано или поздно приведет к ситуации нехватки памяти. В итоге программа существенно замедлит свою работу. Чтобы такого не происходило, с версии Go 1.19 было добавлено ограничение по памяти. + + ### Полное отключение сборщика мусора ## Новый сборщик мусора Green Tea From 698ed68147995d1a32a408ded510df88d6c68547 Mon Sep 17 00:00:00 2001 From: korepanov Date: Thu, 28 May 2026 15:23:35 +0300 Subject: [PATCH 14/24] [+][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 34 +++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index c0ee2a64..2bb2b87a 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -81,7 +81,7 @@ GC Roots — это набор специальных, всегда доступ Вот какие значения принимает `GOGC`. -* значение `off` — установить значение `GOGC` бесконечным. Эта опция часто используется для полного отключения сборщика мусора. Однако одной ее для этого недостаточно. Как отключить сборщик мусора полностью — будет рассмотрено далее. +* значение `off` — установить значение `GOGC` бесконечным. Эта опция часто используется для полного отключения сборщика мусора. Однако одного этого действия не всегда достаточно. Как отключить сборщик мусора полностью — будет рассмотрено далее. * `-1` — аналогично значению `off`. * `0` — сборщик мусора включается почти постоянно. * `[1; 100)` — более частая сборка, экономнее расходует память, но затрачивает больше процессорного времени. @@ -302,8 +302,40 @@ Statistics after the test: Хотя `GOGC` определяет компромисс между использованием памяти и процессорного времени, он не учитывает, что доступная память конечна. Рассмотрим пример. Допустим, произошел кратковременный рост размера кучи. Сборщик мусора оперирует общим размером кучи, пропорционально этому кратковременному значению. Памяти для этого пика может не хватить. Таким образом, `GOGC` необходимо настроить с учетом пикового размера кучи. Даже если в других циклах сборщика мусора для большого значения `GOGC` памяти хватает, мы не можем установить это значение. Хотя эти пики по памяти могут быть редкими, большое значение `GOGC` рано или поздно приведет к ситуации нехватки памяти. В итоге программа существенно замедлит свою работу. Чтобы такого не происходило, с версии Go 1.19 было добавлено ограничение по памяти. +Изначально в качестве ограничения по памяти выставлено `math.MaxInt64` байт. Это `9223372036854775807 байт = ~9 эксабайт`. Можно сказать, что по умолчанию никакого ограничения по памяти нет. Для многих программ это нормально. Однако проблемы возникают, например, с программами на Go в контейнере. В этом случае используемую память ограничивают. + +### Установка значения MemoryLimit + +Есть два способа установить MemoryLimit: + +1. Во время выполнения программы, через функцию `debug.SetMemoryLimit`: + + ```go + package main + + import "runtime/debug" + + func main() { + debug.SetMemoryLimit(1024) + } + ``` + + Функция `debug.SetMemoryLimit` устанавливает ограничение на использование памяти в байтах. В данном случае мы установили `1 КБ`. При установке нового значения функция возвращает предыдущее. Если программа запущена с настройками по умолчанию, то она вернет значение `9223372036854775807`. + +2. Через переменную окружения. Например, в Linux со значением `GOMEMLIMIT=1024`: + + ```bash + GOMEMLIMIT=1024 go run cmd/main.go + ``` + +Отметим одну важную деталь. Если отключить `GOGC` и начать постепенно уменьшать объем доступной памяти, то сборщик мусора будет по-прежнему срабатывать. Он будет включаться по мере достижения размера использованной памяти параметру `GOMEMLIMIT`. Когда объема доступной памяти станет совсем мало, сборщик мусора будет включен почти постоянно. Он будет пытаться поддерживать невыполнимый предел. Такая ситуация называется **«thrashing»**. Она особенно опасна, потому что приводит к зависанию программы. + +Чтобы такого не возникало, ограничение по памяти устанавливается «мягко». Это значит, что сборщик мусора попытается не выйти за параметр `GOMEMLIMIT`, но в критической ситуации памяти все же будет израсходовано чуть больше. ### Полное отключение сборщика мусора + +Чтобы полностью отключить сборщик мусора, нужно выставить `GOGC` в значение `off`, а `GOMEMLIMIT` — в значение `math.MaxInt64`. + ## Новый сборщик мусора Green Tea ## Резюме From 9a824053677ba2973941a51202eb5b7bad5c8953 Mon Sep 17 00:00:00 2001 From: korepanov Date: Thu, 28 May 2026 15:51:13 +0300 Subject: [PATCH 15/24] [+][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 45 ++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 2bb2b87a..95d27e04 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -81,7 +81,7 @@ GC Roots — это набор специальных, всегда доступ Вот какие значения принимает `GOGC`. -* значение `off` — установить значение `GOGC` бесконечным. Эта опция часто используется для полного отключения сборщика мусора. Однако одного этого действия не всегда достаточно. Как отключить сборщик мусора полностью — будет рассмотрено далее. +* значение `off` — установить значение `GOGC` бесконечным. Эта опция часто используется для полного отключения сборщика мусора. * `-1` — аналогично значению `off`. * `0` — сборщик мусора включается почти постоянно. * `[1; 100)` — более частая сборка, экономнее расходует память, но затрачивает больше процессорного времени. @@ -298,6 +298,8 @@ Statistics after the test: Number of GC cycles: 75 ``` +Вывод будет немного различаться от запуска к запуску. + ### Ограничение по памяти MemoryLimit Хотя `GOGC` определяет компромисс между использованием памяти и процессорного времени, он не учитывает, что доступная память конечна. Рассмотрим пример. Допустим, произошел кратковременный рост размера кучи. Сборщик мусора оперирует общим размером кучи, пропорционально этому кратковременному значению. Памяти для этого пика может не хватить. Таким образом, `GOGC` необходимо настроить с учетом пикового размера кучи. Даже если в других циклах сборщика мусора для большого значения `GOGC` памяти хватает, мы не можем установить это значение. Хотя эти пики по памяти могут быть редкими, большое значение `GOGC` рано или поздно приведет к ситуации нехватки памяти. В итоге программа существенно замедлит свою работу. Чтобы такого не происходило, с версии Go 1.19 было добавлено ограничение по памяти. @@ -332,9 +334,46 @@ Statistics after the test: Чтобы такого не возникало, ограничение по памяти устанавливается «мягко». Это значит, что сборщик мусора попытается не выйти за параметр `GOMEMLIMIT`, но в критической ситуации памяти все же будет израсходовано чуть больше. -### Полное отключение сборщика мусора +Что выведет следующий код? В случае ошибки напишите `error`. {.task_text} + +```go {.example_for_playground} +package main + +import ( + "fmt" + "math" + "runtime" + "runtime/debug" +) + +func allocateMemory() [][]byte { + const expNumber = 1000 + var mem [][]byte + for range expNumber { + const mb = 1024 * 1024 + mem = append(mem, make([]byte, mb)) + } + return mem +} + +func main() { + debug.SetGCPercent(-1) + debug.SetMemoryLimit(math.MaxInt64) + allocateMemory() + var m runtime.MemStats + runtime.ReadMemStats(&m) + // Количество циклов сборщика + fmt.Println(m.NumGC) +} +``` + +```consoleoutput {.task_source #golang_chapter_0200_task_0010} +``` +Если выставить `GOGC` в значение `-1`, а `GOMEMLIMIT` — в значение `math.MaxInt64`, то это приведет к полному отключению сборщика мусора. {.task_hint} -Чтобы полностью отключить сборщик мусора, нужно выставить `GOGC` в значение `off`, а `GOMEMLIMIT` — в значение `math.MaxInt64`. +```{.task_answer} +0 +``` ## Новый сборщик мусора Green Tea From b821103b172bb012f43c2e6c4a23fae7f5698a6c Mon Sep 17 00:00:00 2001 From: korepanov Date: Thu, 28 May 2026 16:10:15 +0300 Subject: [PATCH 16/24] [!][golang_chapter_200] text.md --- golang/golang_chapter_0200/text.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 95d27e04..9ca6a9ca 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -31,7 +31,7 @@ Как мы уже сказали, помимо памяти, сборщик мусора затрачивает процессорное время. Время, затраченное за цикл `N`, определяется двумя составляющими. Первая — фиксированное время за цикл. Вторая — произведение среднего времени работы процессора за байт на объем активной памяти кучи в цикле `N`. -Фиксированная время за цикл — это время на те действия, которые выполняются одинаковое количество раз в каждом цикле. Например, инициализации структур для следующего цикла сборки мусора. Это время сравнительно невелико. +Фиксированная время за цикл — это время на те действия, которые выполняются одинаковое количество раз в каждом цикле. Например, инициализация структур для следующего цикла сборки мусора. Это время сравнительно невелико. Большая часть затрат процессора приходится на пометку и сканирование. Средняя стоимость пометки и сканирования зависит от поведения программы. Например, чем больше указателей, тем больше работы, поскольку необходимо посетить все указатели в программе. Связные списки и деревья также сложно обрабатывать, они увеличивают среднюю стоимость байта. @@ -330,7 +330,7 @@ Statistics after the test: GOMEMLIMIT=1024 go run cmd/main.go ``` -Отметим одну важную деталь. Если отключить `GOGC` и начать постепенно уменьшать объем доступной памяти, то сборщик мусора будет по-прежнему срабатывать. Он будет включаться по мере достижения размера использованной памяти параметру `GOMEMLIMIT`. Когда объема доступной памяти станет совсем мало, сборщик мусора будет включен почти постоянно. Он будет пытаться поддерживать невыполнимый предел. Такая ситуация называется **«thrashing»**. Она особенно опасна, потому что приводит к зависанию программы. +Отметим одну важную деталь. Если выставить `GOGC=off` и начать постепенно уменьшать объем доступной памяти, то сборщик мусора будет по-прежнему срабатывать. Он будет включаться по мере достижения размера использованной памяти параметру `GOMEMLIMIT`. Когда объема доступной памяти станет совсем мало, сборщик мусора будет включен почти постоянно. Он будет пытаться поддерживать невыполнимый предел. Такая ситуация называется **«thrashing»**. Она особенно опасна, потому что приводит к зависанию программы. Чтобы такого не возникало, ограничение по памяти устанавливается «мягко». Это значит, что сборщик мусора попытается не выйти за параметр `GOMEMLIMIT`, но в критической ситуации памяти все же будет израсходовано чуть больше. From 57fad3be49aac9cf1d395930467dd397125f5349 Mon Sep 17 00:00:00 2001 From: korepanov Date: Thu, 28 May 2026 17:25:02 +0300 Subject: [PATCH 17/24] [+][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 9ca6a9ca..5fe87ec2 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -2,7 +2,7 @@ Как правило, разработчику на языке Go не приходится задумываться о том, где и как выделяется память. Однако на практике выделение памяти, ее освобождение и повторное использование — непростой процесс. В конечном итоге нужно задействовать физическую память компьютера. Физическая память — ограниченный ресурс, поэтому так важно распоряжаться им разумно. В Go каждое приложение содержит **библиотеку времени выполнения**. Эта библиотека включает в себя инструменты по управлению памятью, в том числе **сборщик мусора**. Сборщик мусора освобождает память, которая больше не нужна, и позволяет использовать ее повторно. -Начиная с версии Go 1.25, появился новый сборщик мусора — **Green Tea**. Сначала он был экспериментальным, но с версии Go 1.26 стал сборщиком мусора по умолчанию. Green Tea — это результат некоторых улучшений старого сборщика мусора. Мы сначала поговорим про старый сборщик мусора, а потом рассмотрим, какие изменения появились в новом. Важно понимать, что все, о чем мы будем говорить про старый сборщик мусора, актуально и для нового. +Начиная с версии Go 1.25, появился новый сборщик мусора — **Green Tea**. Сначала он был экспериментальным, но с версии Go 1.26 стал сборщиком мусора по умолчанию. Green Tea — это результат некоторого улучшения старого сборщика мусора. Мы сначала поговорим про старый сборщик мусора, а потом рассмотрим, какие изменения появились в новом. Важно понимать, что все, о чем мы будем говорить про старый сборщик мусора, актуально и для нового. ## Старый сборщик мусора ### Какую память очищает сборщик мусора @@ -377,4 +377,20 @@ func main() { ## Новый сборщик мусора Green Tea +Начиная с версии языка Go 1.25, его авторы ввели новый сборщик мусора — Green Tea. Он представляет собой некоторое улучшение старого сборщика. Благодаря нему часть программ стала быстрее на 10%, а некоторые — даже на 40%. + +Старые программы на Go часто тратили до 20% процессорного времени на сборку мусора. Разработчики Go решили, что это много. Прежде чем говорить об изменениях в сборщике, коснемся того, на что же уходило это время. + +## Узкое место старого сборщика + +Узким местом старого сборщика является пометка. По оценкам разработчиков Go, 90% процессорного времени тратится на пометку и только 10% на очистку. Из времени, затраченного на пометку, уходит не менее 35% просто на доступ к памяти из кучи. + +Старый сборщик, во время пометки, постоянно перемещается между участками памяти. В результате он выполняется крошечную работу и двигается дальше, часто даже перемещаясь между разными страницами памяти. Современные процессоры используют кеширование. Обращение к кешу до `100` раз быстрее, чем доступ к памяти вне кеша. В кеш попадает та память, к которой недавно произведен доступ. Также там оказываются те участки памяти, которые находятся рядом. Однако нет гарантии, что два объекта, указывающие друг на друга, близко находятся друг к другу в памяти. Процесс сканирования не учитывает этого. + +## Идея Green Tea + +Идея сборщика мусора Green Tea состоит в том, чтобы отслеживать не объекты, а страницы с объектами. Нужно помечать не все объекты подряд, а те объекты, которые находятся на данной странице, а затем переходить к следующей. Вместо того, чтобы просто идти по указателям на объекты, мы перемещаемся по указателям внутри конкретной страницы. Сами же страницы выстраиваются в очередь. Подробнее про алгоритм рассказано на [официальном сайте](https://go.dev/blog/greenteagc) языка. + +Такой прием обеспечивает хорошую совместимость с архитектурой процессора. Теперь мы сканируем близкие друг к другу объекты с большей вероятностью. Таким образом, мы чаще используем кеш. + ## Резюме From 2fd52af376b490dcd7cedde2850162ef30aebcdb Mon Sep 17 00:00:00 2001 From: korepanov Date: Fri, 29 May 2026 12:25:09 +0300 Subject: [PATCH 18/24] [+][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 5fe87ec2..52cce43a 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -114,6 +114,21 @@ GC Roots — это набор специальных, всегда доступ GOGC=off go run cmd/main.go ``` + Команда `go run` создает временный исполняемый файл. Он будет удален после того, как программа отработает. Мы также могли выставить `GOGC=off` и для постоянного исполняемого файла. Чтобы создать постоянный файл, используют команду `go build`: + + ```bash + go build -o prog cmd/main.go + ``` + + Чтобы передать программе все тот же параметр, поступают так: + + ```bash + GOGC=off ./prog + ``` + + Также достаточно просто установить переменную окружения средствами операционной системы. + + Рассмотрим пример. Запустим сервер, который отдает некоторые данные. Функция `generateResponse` генерирует эти данные. Функция `runLoadTest` запускает нагрузочный тест со стороны клиента и получает данные с сервера. Чем больше значение `GOGC`, тем больше запросов в единицу времени успевает обработать сервер. Однако тем больше памяти для этого требуется. Обратите внимание, что и количество циклов сборщика уменьшается по мере увеличения `GOGC`. ```go {.example_for_playground} From 58f7970c015e49eacf36f3e2bfe4f58f57140370 Mon Sep 17 00:00:00 2001 From: korepanov Date: Fri, 29 May 2026 12:41:55 +0300 Subject: [PATCH 19/24] [*][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 52cce43a..02c229f6 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -392,9 +392,7 @@ func main() { ## Новый сборщик мусора Green Tea -Начиная с версии языка Go 1.25, его авторы ввели новый сборщик мусора — Green Tea. Он представляет собой некоторое улучшение старого сборщика. Благодаря нему часть программ стала быстрее на 10%, а некоторые — даже на 40%. - -Старые программы на Go часто тратили до 20% процессорного времени на сборку мусора. Разработчики Go решили, что это много. Прежде чем говорить об изменениях в сборщике, коснемся того, на что же уходило это время. +Начиная с версии языка Go 1.25, его авторы ввели новый сборщик мусора — Green Tea. Он представляет собой некоторое улучшение старого сборщика. Благодаря нему часть программ стала быстрее на 10%, а некоторые — даже на 40%. Старые программы на Go часто тратили до 20% процессорного времени на сборку мусора. Разработчики Go решили, что это много. Прежде чем говорить об изменениях в сборщике, коснемся того, на что же уходило время. ## Узкое место старого сборщика @@ -406,6 +404,8 @@ func main() { Идея сборщика мусора Green Tea состоит в том, чтобы отслеживать не объекты, а страницы с объектами. Нужно помечать не все объекты подряд, а те объекты, которые находятся на данной странице, а затем переходить к следующей. Вместо того, чтобы просто идти по указателям на объекты, мы перемещаемся по указателям внутри конкретной страницы. Сами же страницы выстраиваются в очередь. Подробнее про алгоритм рассказано на [официальном сайте](https://go.dev/blog/greenteagc) языка. -Такой прием обеспечивает хорошую совместимость с архитектурой процессора. Теперь мы сканируем близкие друг к другу объекты с большей вероятностью. Таким образом, мы чаще используем кеш. +Такой прием обеспечивает хорошую совместимость с архитектурой процессора. Теперь мы сканируем близкие друг к другу объекты с большей вероятностью. Таким образом, мы чаще используем кеш. Кроме того, нам стало доступно векторное оборудование. + +## Векторное ускорение ## Резюме From 90b7258ad8a58e32ea6d67ea9c759c2a0e7be894 Mon Sep 17 00:00:00 2001 From: korepanov Date: Fri, 29 May 2026 14:03:01 +0300 Subject: [PATCH 20/24] [+][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 02c229f6..43057193 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -392,7 +392,9 @@ func main() { ## Новый сборщик мусора Green Tea -Начиная с версии языка Go 1.25, его авторы ввели новый сборщик мусора — Green Tea. Он представляет собой некоторое улучшение старого сборщика. Благодаря нему часть программ стала быстрее на 10%, а некоторые — даже на 40%. Старые программы на Go часто тратили до 20% процессорного времени на сборку мусора. Разработчики Go решили, что это много. Прежде чем говорить об изменениях в сборщике, коснемся того, на что же уходило время. +Начиная с версии языка Go 1.25, его авторы ввели новый сборщик мусора — Green Tea. Один из ведущих разработчиков, Остин Клементс, работал над прототипом этого сборщика в 2024 году. В это время он сидел в Японском кафе и попивал матча. В честь этого сборщик мусора получил название «зеленый чай». + +Он представляет собой некоторое улучшение старого сборщика. Благодаря нему часть программ стала быстрее на 10%, а некоторые — даже на 40%. Старые программы на Go часто тратили до 20% процессорного времени на сборку мусора. Разработчики Go решили, что это много. Прежде чем говорить об изменениях в сборщике, коснемся того, на что же уходило время. ## Узкое место старого сборщика @@ -402,10 +404,21 @@ func main() { ## Идея Green Tea -Идея сборщика мусора Green Tea состоит в том, чтобы отслеживать не объекты, а страницы с объектами. Нужно помечать не все объекты подряд, а те объекты, которые находятся на данной странице, а затем переходить к следующей. Вместо того, чтобы просто идти по указателям на объекты, мы перемещаемся по указателям внутри конкретной страницы. Сами же страницы выстраиваются в очередь. Подробнее про алгоритм рассказано на [официальном сайте](https://go.dev/blog/greenteagc) языка. - -Такой прием обеспечивает хорошую совместимость с архитектурой процессора. Теперь мы сканируем близкие друг к другу объекты с большей вероятностью. Таким образом, мы чаще используем кеш. Кроме того, нам стало доступно векторное оборудование. - -## Векторное ускорение - -## Резюме +Идея сборщика мусора Green Tea состоит в том, чтобы отслеживать не объекты, а страницы с объектами. Нужно помечать не все объекты подряд, а те объекты, которые находятся на данной странице, а затем переходить к следующей. Вместо того, чтобы просто идти по указателям на объекты, мы перемещаемся по указателям внутри конкретной страницы. Сами же страницы выстраиваются в очередь. Такой прием обеспечивает хорошую совместимость с архитектурой процессора. Мы сканируем близкие друг к другу объекты с большей вероятностью. Таким образом, мы чаще используем кеш. Подробнее про алгоритм рассказано на [официальном сайте](https://go.dev/blog/greenteagc) языка. + +## Резюме + +1. Сборщик мусора освобождает память от недоступных объектов. Тем самым мы можем использовать память повторно. +2. Цикл сборки мусора состоит из пометки, очистки и выключения. За время работы программы сборщик постоянно выполняет цикл за циклом. +3. Стоимость на сборку мусора — это затраты физической памяти и процессорного времени. +4. Чем больше памяти мы затрачиваем, тем меньше процессорного времени требуется сборщику, и наоборот. Параметр `GOGC` определяет компромисс между использованием процессорного времени и памяти. +5. `GOGC` — это процент того, насколько куча может временно разрастись за счет мусора. +6. Значение `GOGC` по умолчанию равно `100`. Это значение хорошо подходит для многих программ, однако есть способ его изменить. +7. Изменяют значение `GOGC` либо через функцию `debug.SetGCPercent`, либо через переменную окружения `GOGC`. +8. Существует возможность установить ограничение по памяти для сборщика мусора. Оно выражается в виде абсолютного значения, в байтах. +9. По умолчанию ограничение по памяти отсутствует. +10. Устанавливают ограничение по памяти либо через функцию `debug.SetMemoryLimit`, либо через переменную окружения `GOMEMLIMIT`. +11. Узкое место старого сборщика мусора — режим пометки. Этот сборщик не учитывает локальность памяти. Проблему решает новый сборщик мусора Green Tea. +12. Сборщик мусора Green Tea — результат улучшения старого сборщика, а не принципиально новое решение. +13. Green Tea сканирует не все объекты подряд, а делает это постранично. +14. Green Tea увеличивает быстродействие части программ на 10% — 40%. From ebb227895ca530c499bfc147a339a1c9179b8459 Mon Sep 17 00:00:00 2001 From: korepanov Date: Fri, 29 May 2026 14:38:54 +0300 Subject: [PATCH 21/24] [!][golang_chapter_0200] text.md --- golang/golang_chapter_0200/text.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 43057193..20dbcec6 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -25,7 +25,7 @@ Сборщик мусора использует только два ресурса: физическую память и процессорное время. -Затраты на память состоят из активной памяти кучи, новой памяти кучи и метаданных. Метаданные довольно малы по сравнению с остальными затратами на память. Активная память кучи — это память, которая была помечена сборщиком мусора в предыдущем цикле. Новая память кучи — это память, выделенная в текущем цикле, которая может стать активной или нет к концу цикла. Объем активной памяти является свойством программы, а не чем-то, что сборщик мусора может контролировать. +Затраты на память состоят из метаданных, активной памяти кучи и новой памяти кучи. Метаданные довольно малы по сравнению с остальными затратами на память. Активная память кучи — это память, которая была помечена сборщиком мусора в предыдущем цикле. Новая память кучи — это память, выделенная в текущем цикле, которая может стать активной или нет к концу цикла. Объем активной памяти является свойством программы, а не чем-то, что сборщик мусора может контролировать. Стоимость памяти для цикла `N` сборщика мусора определяется количеством активной памяти кучи из цикла `N-1` и новой памяти. @@ -394,7 +394,7 @@ func main() { Начиная с версии языка Go 1.25, его авторы ввели новый сборщик мусора — Green Tea. Один из ведущих разработчиков, Остин Клементс, работал над прототипом этого сборщика в 2024 году. В это время он сидел в Японском кафе и попивал матча. В честь этого сборщик мусора получил название «зеленый чай». -Он представляет собой некоторое улучшение старого сборщика. Благодаря нему часть программ стала быстрее на 10%, а некоторые — даже на 40%. Старые программы на Go часто тратили до 20% процессорного времени на сборку мусора. Разработчики Go решили, что это много. Прежде чем говорить об изменениях в сборщике, коснемся того, на что же уходило время. +Green Tea представляет собой некоторое улучшение старого сборщика. Благодаря нему часть программ стала быстрее на 10%, а некоторые — даже на 40%. Старые программы на Go часто тратили до 20% процессорного времени на сборку мусора. Разработчики Go решили, что это много. Новый сборщик мусора тратит на 10% меньше. Прежде чем говорить об изменениях в сборщике, коснемся того, на что же уходило время. ## Узкое место старого сборщика From 401bfa36b1a32c5c59aa30ff5b1d3627161c8ca7 Mon Sep 17 00:00:00 2001 From: korepanov Date: Mon, 1 Jun 2026 16:39:08 +0300 Subject: [PATCH 22/24] [!][golang_chapter_0200] review --- golang/golang_chapter_0200/text.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 20dbcec6..4e6f6bf9 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -2,18 +2,21 @@ Как правило, разработчику на языке Go не приходится задумываться о том, где и как выделяется память. Однако на практике выделение памяти, ее освобождение и повторное использование — непростой процесс. В конечном итоге нужно задействовать физическую память компьютера. Физическая память — ограниченный ресурс, поэтому так важно распоряжаться им разумно. В Go каждое приложение содержит **библиотеку времени выполнения**. Эта библиотека включает в себя инструменты по управлению памятью, в том числе **сборщик мусора**. Сборщик мусора освобождает память, которая больше не нужна, и позволяет использовать ее повторно. -Начиная с версии Go 1.25, появился новый сборщик мусора — **Green Tea**. Сначала он был экспериментальным, но с версии Go 1.26 стал сборщиком мусора по умолчанию. Green Tea — это результат некоторого улучшения старого сборщика мусора. Мы сначала поговорим про старый сборщик мусора, а потом рассмотрим, какие изменения появились в новом. Важно понимать, что все, о чем мы будем говорить про старый сборщик мусора, актуально и для нового. +Начиная с версии Go 1.25, появился новый сборщик мусора — **Green Tea**. Сначала он был экспериментальным, но с версии Go 1.26 стал сборщиком мусора по умолчанию. Green Tea — это результат улучшения старого сборщика мусора. Мы сначала поговорим про старый сборщик мусора, а потом рассмотрим, какие изменения появились в новом. Важно понимать, что все, о чем мы будем говорить про старый сборщик мусора, актуально и для нового. ## Старый сборщик мусора ### Какую память очищает сборщик мусора Прежде чем рассматривать сборщик мусора, поговорим о памяти, которую сборщик мусора **не** очищает. Сборщик мусора не очищает значения локальных переменных, которые не являются указателями. Такие переменные хранятся на **стеке** горутины. Компилятор Go сам может определить, когда их необходимо очистить. Для этого он генерирует соответствующие машинные инструкции. -Есть переменные, время жизни которых компилятор определить не способен. Например, возвращаемые из функции переменные. Они живут дольше функции: неизвестно, когда их нужно уничтожить. Существуют большие значения, которые не помещаются в стек. Например, объемный массив. Иногда мы и вовсе не знаем о размере значения. Например, для базового массива среза, начальный размер которого определяется переменной, а не константой. Память в таких ситуациях выделяется динамически. Все эти значения помещаются в **кучу**. Именно с ними работает сборщик мусора. +Есть переменные, время жизни которых компилятор определить не способен. Скажем, возвращаемые из функции переменные: неизвестно, когда их нужно уничтожить. Объемный массив вообще не поместится в стек. Иногда мы и вовсе не знаем о том, сколько памяти нам потребуется. Например, для базового массива среза, начальный размер которого определяется переменной, а не константой. Память в таких ситуациях выделяется динамически. Все эти значения помещаются в **кучу**. Именно с ними работает сборщик мусора. ### Трассировка сборщика мусора +Разные сборщики мусора по-разному определяют память, которую необходимо высвободить. Например, часть из них ведут подсчет ссылок. Как только счетчик ссылок на некоторую область памяти становится равным нулю, память освобождается. -Сборщик мусора в Go относится к **трассирующим сборщикам**. Он определяет используемые объекты, следуя по указателям. **Объект** в нашей терминологии — это динамически выделенный участок памяти, который содержит одно или несколько значений. **Указатель** — это адрес любого значения внутри объекта. Указатель — это, например, переменные типа `*int` или `*float`. Также это строки, срезы, каналы, хеш-таблицы и значения интерфейсов. Вместе объекты и указатели образуют граф. Сборщик мусора проходится по этому графу и отмечает те объекты, которые программа использует. Процесс обхода графа называется **сканированием**. После сканирования сборщик мусора проходит по всей памяти в куче и делает доступной всю не помеченную память. +Сборщик мусора в Go другой. Он относится к **трассирующим сборщикам**. Трассировка — это процесс отслеживания некоторого пути. Сборщик определяет используемые объекты, следуя по указателям. **Объект** в нашей терминологии — это динамически выделенный участок памяти, который содержит одно или несколько значений. **Указатель** — это адрес любого значения внутри объекта. Указатель — это, например, переменные типа `*int` или `*float`. Также это строки, срезы, каналы, хеш-таблицы и значения интерфейсов. Вместе объекты и указатели образуют граф. + +Сборщик мусора проходится по этому графу и отмечает те объекты, которые программа использует. Процесс обхода графа называется **сканированием**. После сканирования сборщик мусора проходит по всей памяти в куче и делает доступной всю не помеченную память. ### Цикл сборки мусора @@ -31,7 +34,7 @@ Как мы уже сказали, помимо памяти, сборщик мусора затрачивает процессорное время. Время, затраченное за цикл `N`, определяется двумя составляющими. Первая — фиксированное время за цикл. Вторая — произведение среднего времени работы процессора за байт на объем активной памяти кучи в цикле `N`. -Фиксированная время за цикл — это время на те действия, которые выполняются одинаковое количество раз в каждом цикле. Например, инициализация структур для следующего цикла сборки мусора. Это время сравнительно невелико. +Фиксированное время за цикл — это время на те действия, которые выполняются одинаковое количество раз в каждом цикле. Например, инициализация структур для следующего цикла сборки мусора. Это время сравнительно невелико. Большая часть затрат процессора приходится на пометку и сканирование. Средняя стоимость пометки и сканирования зависит от поведения программы. Например, чем больше указателей, тем больше работы, поскольку необходимо посетить все указатели в программе. Связные списки и деревья также сложно обрабатывать, они увеличивают среднюю стоимость байта. @@ -394,11 +397,11 @@ func main() { Начиная с версии языка Go 1.25, его авторы ввели новый сборщик мусора — Green Tea. Один из ведущих разработчиков, Остин Клементс, работал над прототипом этого сборщика в 2024 году. В это время он сидел в Японском кафе и попивал матча. В честь этого сборщик мусора получил название «зеленый чай». -Green Tea представляет собой некоторое улучшение старого сборщика. Благодаря нему часть программ стала быстрее на 10%, а некоторые — даже на 40%. Старые программы на Go часто тратили до 20% процессорного времени на сборку мусора. Разработчики Go решили, что это много. Новый сборщик мусора тратит на 10% меньше. Прежде чем говорить об изменениях в сборщике, коснемся того, на что же уходило время. +Green Tea не является чем-то принципиально другим. Это модификация существующего сборщика мусора. Благодаря нему часть программ стала быстрее на 10%, а некоторые — даже на 40%. Старые программы на Go часто тратили до 20% процессорного времени на сборку мусора. Разработчики Go решили, что это много. Новый сборщик мусора тратит на 10% меньше. Прежде чем говорить об изменениях в сборщике, коснемся того, на что же уходило время. -## Узкое место старого сборщика +## Бутылочное горлышко старого сборщика -Узким местом старого сборщика является пометка. По оценкам разработчиков Go, 90% процессорного времени тратится на пометку и только 10% на очистку. Из времени, затраченного на пометку, уходит не менее 35% просто на доступ к памяти из кучи. +Бутылочным горлышком старого сборщика является пометка. По оценкам разработчиков Go, 90% процессорного времени тратится на пометку и только 10% на очистку. Из времени, затраченного на пометку, уходит не менее 35% просто на доступ к памяти из кучи. Старый сборщик, во время пометки, постоянно перемещается между участками памяти. В результате он выполняется крошечную работу и двигается дальше, часто даже перемещаясь между разными страницами памяти. Современные процессоры используют кеширование. Обращение к кешу до `100` раз быстрее, чем доступ к памяти вне кеша. В кеш попадает та память, к которой недавно произведен доступ. Также там оказываются те участки памяти, которые находятся рядом. Однако нет гарантии, что два объекта, указывающие друг на друга, близко находятся друг к другу в памяти. Процесс сканирования не учитывает этого. @@ -418,7 +421,7 @@ Green Tea представляет собой некоторое улучшен 8. Существует возможность установить ограничение по памяти для сборщика мусора. Оно выражается в виде абсолютного значения, в байтах. 9. По умолчанию ограничение по памяти отсутствует. 10. Устанавливают ограничение по памяти либо через функцию `debug.SetMemoryLimit`, либо через переменную окружения `GOMEMLIMIT`. -11. Узкое место старого сборщика мусора — режим пометки. Этот сборщик не учитывает локальность памяти. Проблему решает новый сборщик мусора Green Tea. +11. Бутылочное горлышко старого сборщика мусора — режим пометки. Этот сборщик не учитывает локальность памяти. Проблему решает новый сборщик мусора Green Tea. 12. Сборщик мусора Green Tea — результат улучшения старого сборщика, а не принципиально новое решение. 13. Green Tea сканирует не все объекты подряд, а делает это постранично. 14. Green Tea увеличивает быстродействие части программ на 10% — 40%. From d782a7dc3023e43ac3457b22599d81ea28b36be1 Mon Sep 17 00:00:00 2001 From: korepanov Date: Mon, 1 Jun 2026 17:31:52 +0300 Subject: [PATCH 23/24] [!][golang_chapter_200] text.md --- golang/golang_chapter_0200/text.md | 52 +++--------------------------- 1 file changed, 5 insertions(+), 47 deletions(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 4e6f6bf9..952e2314 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -16,7 +16,7 @@ Сборщик мусора в Go другой. Он относится к **трассирующим сборщикам**. Трассировка — это процесс отслеживания некоторого пути. Сборщик определяет используемые объекты, следуя по указателям. **Объект** в нашей терминологии — это динамически выделенный участок памяти, который содержит одно или несколько значений. **Указатель** — это адрес любого значения внутри объекта. Указатель — это, например, переменные типа `*int` или `*float`. Также это строки, срезы, каналы, хеш-таблицы и значения интерфейсов. Вместе объекты и указатели образуют граф. -Сборщик мусора проходится по этому графу и отмечает те объекты, которые программа использует. Процесс обхода графа называется **сканированием**. После сканирования сборщик мусора проходит по всей памяти в куче и делает доступной всю не помеченную память. +Сборщик мусора проходится по этому графу и помечает те объекты, которые программа использует. Процесс обхода графа называется **сканированием**. После сканирования сборщик мусора проходит по всей куче и делает доступной всю не помеченную память. ### Цикл сборки мусора @@ -24,61 +24,19 @@ ### Затраты на сборку мусора -В этом разделе будет показано, как оценить стоимость на сборку мусора. - Сборщик мусора использует только два ресурса: физическую память и процессорное время. -Затраты на память состоят из метаданных, активной памяти кучи и новой памяти кучи. Метаданные довольно малы по сравнению с остальными затратами на память. Активная память кучи — это память, которая была помечена сборщиком мусора в предыдущем цикле. Новая память кучи — это память, выделенная в текущем цикле, которая может стать активной или нет к концу цикла. Объем активной памяти является свойством программы, а не чем-то, что сборщик мусора может контролировать. - -Стоимость памяти для цикла `N` сборщика мусора определяется количеством активной памяти кучи из цикла `N-1` и новой памяти. - -Как мы уже сказали, помимо памяти, сборщик мусора затрачивает процессорное время. Время, затраченное за цикл `N`, определяется двумя составляющими. Первая — фиксированное время за цикл. Вторая — произведение среднего времени работы процессора за байт на объем активной памяти кучи в цикле `N`. - -Фиксированное время за цикл — это время на те действия, которые выполняются одинаковое количество раз в каждом цикле. Например, инициализация структур для следующего цикла сборки мусора. Это время сравнительно невелико. - -Большая часть затрат процессора приходится на пометку и сканирование. Средняя стоимость пометки и сканирования зависит от поведения программы. Например, чем больше указателей, тем больше работы, поскольку необходимо посетить все указатели в программе. Связные списки и деревья также сложно обрабатывать, они увеличивают среднюю стоимость байта. +Большая часть затрат процессора приходится на пометку и сканирование. Средняя стоимость пометки и сканирования зависит от поведения программы. Например, чем больше указателей, тем больше работы, поскольку необходимо посетить все указатели в программе. Связные списки и деревья также сложно обрабатывать. Затраченное процессорное время зависит от общего количества циклов сборщика мусора за заданный промежуток времени. Кроме того, чем чаще запускается сборщик мусора, тем меньше памяти он тратит. Однако тем больше мы расходуем процессорного времени. Как найти компромисс между тем и другим? За то, как часто запускать сборщик мусора, отвечает параметр **GOGC**. ### GOGC -`GOGC` определяет компромисс между производительностью сборщика мусора и памятью, которую он использует. Он определяет целевой размер кучи после каждого цикла сборки мусора. Сборщик мусора должен завершить цикл сборки мусора до того, как общий размер кучи превысит целевой. Общий размер кучи складывается из размера активной и новой памяти кучи за предыдущий цикл. Чтобы понять, как рассчитывается целевой размер кучи, нам нужно сначала ввести понятие **GC roots**. - -GC Roots — это набор специальных, всегда доступных «исходных точек» в памяти программы. С них сборщик мусора начинает свой обход. К ним относятся глобальные переменные и стеки вызовов горутин. - -Целевой размер кучи определяется формулой: - -``` -Ц = А + (А + К) * Г / 100 -``` - -Здесь -* `Ц` — целевой размер кучи, -* `А` — размер активный кучи, -* `К` — размер GC Roots, -* `Г` — параметр `GOGC`. - -Рассмотрим пример. Пусть в программе на Go размер активной кучи равен 8 Мб, размер стеков горутин — 1 Мб и размер памяти, которая доступна по глобальным указателям, — 1 Мб. Допустим, `GOGC` равен 100. Тогда целевой размер кучи: - -``` -Ц = 8 + (8 + (1 + 1)) * 100 / 100 = 18 Мб -``` - -Когда `GOGC` равен 50: - -``` -Ц = 8 + (8 + (1 + 1)) * 50 / 100 = 13 Мб -``` - -Когда `GOGC` равен 200: - -``` -Ц = 8 + (8 + (1 + 1)) * 200 / 100 = 28 Мб -``` +`GOGC` определяет компромисс между производительностью сборщика мусора и памятью, которую он использует. Он определяет целевой размер кучи после каждого цикла сборки мусора. Сборщик мусора должен завершить цикл сборки мусора до того, как общий размер кучи превысит целевой. Чем больше `GOGC`, тем больше целевой размер кучи. В свою очередь чем больше целевой размер кучи, тем реже нужно запускаться сборщику мусора. Тем больше памяти и меньше процессорного времени он потребляет. И наоборот. Говоря проще, чем больше значение `GOGC`, тем больше памяти и меньше процессорного времени расходуется. Чем меньше `GOGC`, тем меньше памяти и больше процессорного времени будет потрачено. -Если пренебречь размером GC Roots, то можно сказать, что `GOGC` — это целевое значение для роста активной кучи, выраженное в процентах. Другими словами, то, насколько куча может временно разрастись за счет мусора. +`GOGC` — это целевое значение для роста кучи, выраженное в процентах. Другими словами, то, насколько куча может временно разрастись за счет мусора. ### Значения GOGC @@ -403,7 +361,7 @@ Green Tea не является чем-то принципиально друг Бутылочным горлышком старого сборщика является пометка. По оценкам разработчиков Go, 90% процессорного времени тратится на пометку и только 10% на очистку. Из времени, затраченного на пометку, уходит не менее 35% просто на доступ к памяти из кучи. -Старый сборщик, во время пометки, постоянно перемещается между участками памяти. В результате он выполняется крошечную работу и двигается дальше, часто даже перемещаясь между разными страницами памяти. Современные процессоры используют кеширование. Обращение к кешу до `100` раз быстрее, чем доступ к памяти вне кеша. В кеш попадает та память, к которой недавно произведен доступ. Также там оказываются те участки памяти, которые находятся рядом. Однако нет гарантии, что два объекта, указывающие друг на друга, близко находятся друг к другу в памяти. Процесс сканирования не учитывает этого. +Старый сборщик, во время пометки, постоянно перемещается между участками памяти. В результате он выполняет крошечную работу и двигается дальше, часто даже перемещаясь между разными страницами памяти. Современные процессоры используют кеширование. Обращение к кешу до `100` раз быстрее, чем доступ к памяти вне кеша. В кеш попадает та память, к которой недавно произведен доступ. Также там оказываются те участки памяти, которые находятся рядом. Однако нет гарантии, что два объекта, указывающие друг на друга, близко находятся друг к другу в памяти. Процесс сканирования не учитывает этого. ## Идея Green Tea From 2fc38e1e6b35c6a4bc6e152125c32c313c150c00 Mon Sep 17 00:00:00 2001 From: korepanov Date: Thu, 11 Jun 2026 10:41:10 +0300 Subject: [PATCH 24/24] [!][golang_chapter_0200] text.md: review --- golang/golang_chapter_0200/text.md | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/golang/golang_chapter_0200/text.md b/golang/golang_chapter_0200/text.md index 952e2314..db9aa047 100644 --- a/golang/golang_chapter_0200/text.md +++ b/golang/golang_chapter_0200/text.md @@ -1,28 +1,25 @@ # Глава 20. Сборщик мусора Как правило, разработчику на языке Go не приходится задумываться о том, где и как выделяется память. Однако на практике выделение памяти, ее освобождение и повторное использование — непростой процесс. В конечном итоге нужно задействовать физическую память компьютера. Физическая память — ограниченный ресурс, поэтому так важно распоряжаться им разумно. В Go каждое приложение содержит **библиотеку времени выполнения**. Эта библиотека включает в себя инструменты по управлению памятью, в том числе **сборщик мусора**. Сборщик мусора освобождает память, которая больше не нужна, и позволяет использовать ее повторно. - -Начиная с версии Go 1.25, появился новый сборщик мусора — **Green Tea**. Сначала он был экспериментальным, но с версии Go 1.26 стал сборщиком мусора по умолчанию. Green Tea — это результат улучшения старого сборщика мусора. Мы сначала поговорим про старый сборщик мусора, а потом рассмотрим, какие изменения появились в новом. Важно понимать, что все, о чем мы будем говорить про старый сборщик мусора, актуально и для нового. - -## Старый сборщик мусора -### Какую память очищает сборщик мусора + +## Какую память очищает сборщик мусора Прежде чем рассматривать сборщик мусора, поговорим о памяти, которую сборщик мусора **не** очищает. Сборщик мусора не очищает значения локальных переменных, которые не являются указателями. Такие переменные хранятся на **стеке** горутины. Компилятор Go сам может определить, когда их необходимо очистить. Для этого он генерирует соответствующие машинные инструкции. Есть переменные, время жизни которых компилятор определить не способен. Скажем, возвращаемые из функции переменные: неизвестно, когда их нужно уничтожить. Объемный массив вообще не поместится в стек. Иногда мы и вовсе не знаем о том, сколько памяти нам потребуется. Например, для базового массива среза, начальный размер которого определяется переменной, а не константой. Память в таких ситуациях выделяется динамически. Все эти значения помещаются в **кучу**. Именно с ними работает сборщик мусора. -### Трассировка сборщика мусора +## Трассировка сборщика мусора Разные сборщики мусора по-разному определяют память, которую необходимо высвободить. Например, часть из них ведут подсчет ссылок. Как только счетчик ссылок на некоторую область памяти становится равным нулю, память освобождается. Сборщик мусора в Go другой. Он относится к **трассирующим сборщикам**. Трассировка — это процесс отслеживания некоторого пути. Сборщик определяет используемые объекты, следуя по указателям. **Объект** в нашей терминологии — это динамически выделенный участок памяти, который содержит одно или несколько значений. **Указатель** — это адрес любого значения внутри объекта. Указатель — это, например, переменные типа `*int` или `*float`. Также это строки, срезы, каналы, хеш-таблицы и значения интерфейсов. Вместе объекты и указатели образуют граф. Сборщик мусора проходится по этому графу и помечает те объекты, которые программа использует. Процесс обхода графа называется **сканированием**. После сканирования сборщик мусора проходит по всей куче и делает доступной всю не помеченную память. -### Цикл сборки мусора +## Цикл сборки мусора Как мы уже поняли, сборщик мусора может работать в режиме пометки и режиме очистки памяти. Процессы пометки и очистки не могут быть выполнены параллельно. Пока не поймем, что на данный объект не ссылается ни один указатель, мы не можем очистить память. Кроме того, сборщик мусора бывает вообще не активен, когда для него нет никакой работы. Таким образом, сборщик мусора находится в одном из трех состояний: пометка, очистка и выключение. Непрерывный переход из одного состояние в другое называется **циклом сборки мусора**. -### Затраты на сборку мусора +## Затраты на сборку мусора Сборщик мусора использует только два ресурса: физическую память и процессорное время. @@ -30,7 +27,7 @@ Затраченное процессорное время зависит от общего количества циклов сборщика мусора за заданный промежуток времени. Кроме того, чем чаще запускается сборщик мусора, тем меньше памяти он тратит. Однако тем больше мы расходуем процессорного времени. Как найти компромисс между тем и другим? За то, как часто запускать сборщик мусора, отвечает параметр **GOGC**. -### GOGC +## GOGC `GOGC` определяет компромисс между производительностью сборщика мусора и памятью, которую он использует. Он определяет целевой размер кучи после каждого цикла сборки мусора. Сборщик мусора должен завершить цикл сборки мусора до того, как общий размер кучи превысит целевой. @@ -38,7 +35,7 @@ `GOGC` — это целевое значение для роста кучи, выраженное в процентах. Другими словами, то, насколько куча может временно разрастись за счет мусора. -### Значения GOGC +## Значения GOGC Вот какие значения принимает `GOGC`. @@ -51,7 +48,7 @@ Максимальное значение `GOGC` зависит от конкретных реализаций сборщика мусора и платформы, на которой выполняется программа. На практике гигантские значения `GOGC` не имеют смысла. Для этого есть `off` и `-1`. -### Установка значения GOGC +## Установка значения GOGC Есть два способа установить значение `GOGC`: @@ -276,13 +273,13 @@ Statistics after the test: Вывод будет немного различаться от запуска к запуску. -### Ограничение по памяти MemoryLimit +## Ограничение по памяти MemoryLimit Хотя `GOGC` определяет компромисс между использованием памяти и процессорного времени, он не учитывает, что доступная память конечна. Рассмотрим пример. Допустим, произошел кратковременный рост размера кучи. Сборщик мусора оперирует общим размером кучи, пропорционально этому кратковременному значению. Памяти для этого пика может не хватить. Таким образом, `GOGC` необходимо настроить с учетом пикового размера кучи. Даже если в других циклах сборщика мусора для большого значения `GOGC` памяти хватает, мы не можем установить это значение. Хотя эти пики по памяти могут быть редкими, большое значение `GOGC` рано или поздно приведет к ситуации нехватки памяти. В итоге программа существенно замедлит свою работу. Чтобы такого не происходило, с версии Go 1.19 было добавлено ограничение по памяти. Изначально в качестве ограничения по памяти выставлено `math.MaxInt64` байт. Это `9223372036854775807 байт = ~9 эксабайт`. Можно сказать, что по умолчанию никакого ограничения по памяти нет. Для многих программ это нормально. Однако проблемы возникают, например, с программами на Go в контейнере. В этом случае используемую память ограничивают. -### Установка значения MemoryLimit +## Установка значения MemoryLimit Есть два способа установить MemoryLimit: