├── go.mod ├── README.md ├── Dockerfile ├── docker-compose.yml ├── cmd ├── 7_simple_pool │ └── main.go ├── 1_escape_analysis │ └── main.go ├── 8_arena │ └── main.go ├── 5_gogc_real_life │ └── main.go ├── 3_gogc_10 │ └── main.go ├── 4_gogc_1000 │ └── main.go ├── 2_gogc_100 │ └── main.go └── 6_gogc_1000_gomemlimit │ └── main.go └── .gitignore /go.mod: -------------------------------------------------------------------------------- 1 | module gc 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Garbage collector 2 | Repo with examples for [article](https://medium.com/better-programming/memory-optimization-and-garbage-collector-management-in-go-71da4612a960). -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:latest as builder 2 | 3 | WORKDIR /src 4 | COPY . . 5 | 6 | RUN GOOS=linux go build -o app ./cmd/4_gogc_1000/ 7 | FROM golang:latest 8 | WORKDIR /root/ 9 | COPY --from=builder /src/app . 10 | EXPOSE 8080 11 | CMD ["./app"] -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | gc: 4 | environment: 5 | GOMEMLIMIT: "8MiB" 6 | build: 7 | context: . 8 | dockerfile: Dockerfile 9 | ports: 10 | - 8080:8080 11 | deploy: 12 | resources: 13 | limits: 14 | memory: 10M 15 | -------------------------------------------------------------------------------- /cmd/7_simple_pool/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | pool := sync.Pool{ 10 | New: func() interface{} { 11 | return []int{} 12 | }, 13 | } 14 | 15 | // Получение объекта из пула 16 | obj := pool.Get().([]int) 17 | obj = []int{42} 18 | fmt.Println("Получен объект из пула:", obj) 19 | 20 | // Возврат объекта в пул 21 | pool.Put(obj) 22 | fmt.Println("Объект возвращен в пул") 23 | } 24 | -------------------------------------------------------------------------------- /cmd/1_escape_analysis/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "runtime" 4 | 5 | // Run with: go run -gcflags=-m cmd/1_escape_analysis/main.go 6 | 7 | func main() { 8 | var arrayBefore10Mb [1310720]int // Меньше 10 МБ 9 | arrayBefore10Mb[0] = 1 10 | 11 | var arrayAfter10Mb [1310721]int // Больше 10 МБ 12 | arrayAfter10Mb[0] = 1 13 | 14 | sliceBefore64 := make([]int, 8192) // Меньше 64 КБ 15 | sliceOver64 := make([]int, 8193) // Больше 64 КБ 16 | sliceOver64[0] = sliceBefore64[0] 17 | 18 | runtime.GC() 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | .idea -------------------------------------------------------------------------------- /cmd/8_arena/main.go: -------------------------------------------------------------------------------- 1 | //go:build goexperiment.arenas 2 | // +build goexperiment.arenas 3 | 4 | package main 5 | 6 | import ( 7 | "arena" 8 | "fmt" 9 | "os" 10 | "runtime/trace" 11 | "time" 12 | ) 13 | 14 | const ( 15 | numberOfArrays = 100 16 | arrayLen = 2000 17 | ) 18 | 19 | func main() { 20 | // Запись в trace файл 21 | f, _ := os.Create("trace.out") 22 | trace.Start(f) 23 | defer trace.Stop() 24 | 25 | mem := arena.NewArena() 26 | for j := 0; j < numberOfArrays; j++ { 27 | o1 := arena.MakeSlice[float64](mem, arrayLen, arrayLen) 28 | for i := 0; i < arrayLen; i++ { 29 | o1[i] = float64(i) 30 | } 31 | time.Sleep(10 * time.Millisecond) 32 | } 33 | t := arena.New[int](mem) 34 | value := 42 35 | t = &value 36 | fmt.Println(*t) 37 | mem.Free() 38 | } 39 | -------------------------------------------------------------------------------- /cmd/5_gogc_real_life/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime" 7 | "runtime/debug" 8 | "runtime/trace" 9 | "sync" 10 | ) 11 | 12 | const ( 13 | NumWorkers = 4 // Количество воркеров 14 | NumTasks = 500 // Количество задач 15 | MemoryIntense = 1000000 // Размер память затратной задачи (число элементов) 16 | ) 17 | 18 | func main() { 19 | runtime.GOMAXPROCS(2) 20 | 21 | // Запись в trace файл 22 | f, _ := os.Create("trace.out") 23 | trace.Start(f) 24 | defer trace.Stop() 25 | 26 | // Установка целевого процента сборщика мусора. По умолчанию 100 27 | debug.SetGCPercent(1000) 28 | 29 | // Очередь задач и очередь результата 30 | taskQueue := make(chan int, NumTasks) 31 | resultQueue := make(chan int, NumTasks) 32 | 33 | // Запуск воркеров 34 | var wg sync.WaitGroup 35 | wg.Add(NumWorkers) 36 | for i := 0; i < NumWorkers; i++ { 37 | go worker(taskQueue, resultQueue, &wg) 38 | } 39 | 40 | // Отправка задач в очередь 41 | for i := 0; i < NumTasks; i++ { 42 | taskQueue <- i 43 | } 44 | close(taskQueue) 45 | 46 | // Получение результатов из очереди 47 | go func() { 48 | wg.Wait() 49 | close(resultQueue) 50 | }() 51 | 52 | // Обработка результатов 53 | for result := range resultQueue { 54 | fmt.Println("Результат:", result) 55 | } 56 | 57 | fmt.Println("Готово!") 58 | } 59 | 60 | // Функция воркера 61 | func worker(tasks <-chan int, results chan<- int, wg *sync.WaitGroup) { 62 | defer wg.Done() 63 | 64 | for task := range tasks { 65 | result := performMemoryIntensiveTask(task) 66 | results <- result 67 | } 68 | } 69 | 70 | // performMemoryIntensiveTask функция требующая много памяти 71 | func performMemoryIntensiveTask(task int) int { 72 | // Создание среза большого размера 73 | data := make([]int, MemoryIntense) 74 | for i := 0; i < MemoryIntense; i++ { 75 | data[i] = i + task 76 | } 77 | 78 | // Вычисление результата 79 | result := 0 80 | for _, value := range data { 81 | result += value 82 | } 83 | return result 84 | } 85 | -------------------------------------------------------------------------------- /cmd/3_gogc_10/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime/debug" 7 | "runtime/trace" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | const ( 13 | NumWorkers = 4 // Количество воркеров 14 | NumTasks = 500 // Количество задач 15 | MemoryIntense = 10000 // Размер память затратной задачи (число элементов) 16 | ) 17 | 18 | func main() { 19 | // Запись в trace файл 20 | f, _ := os.Create("trace.out") 21 | trace.Start(f) 22 | defer trace.Stop() 23 | 24 | // Установка целевого процента сборщика мусора GOGC=10% 25 | debug.SetGCPercent(10) 26 | 27 | // Очередь задач и очередь результата 28 | taskQueue := make(chan int, NumTasks) 29 | resultQueue := make(chan int, NumTasks) 30 | 31 | // Запуск воркеров 32 | var wg sync.WaitGroup 33 | wg.Add(NumWorkers) 34 | for i := 0; i < NumWorkers; i++ { 35 | go worker(taskQueue, resultQueue, &wg) 36 | } 37 | 38 | // Отправка задач в очередь 39 | for i := 0; i < NumTasks; i++ { 40 | taskQueue <- i 41 | } 42 | close(taskQueue) 43 | 44 | // Получение результатов из очереди 45 | go func() { 46 | wg.Wait() 47 | close(resultQueue) 48 | }() 49 | 50 | // Обработка результатов 51 | for result := range resultQueue { 52 | fmt.Println("Результат:", result) 53 | } 54 | 55 | fmt.Println("Готово!") 56 | } 57 | 58 | // Функция воркера 59 | func worker(tasks <-chan int, results chan<- int, wg *sync.WaitGroup) { 60 | defer wg.Done() 61 | 62 | for task := range tasks { 63 | result := performMemoryIntensiveTask(task) 64 | results <- result 65 | } 66 | } 67 | 68 | // performMemoryIntensiveTask функция требующая много памяти 69 | func performMemoryIntensiveTask(task int) int { 70 | // Создание среза большого размера 71 | data := make([]int, MemoryIntense) 72 | for i := 0; i < MemoryIntense; i++ { 73 | data[i] = i + task 74 | } 75 | 76 | // Имитация временной задержки 77 | time.Sleep(10 * time.Millisecond) 78 | 79 | // Вычисление результата 80 | result := 0 81 | for _, value := range data { 82 | result += value 83 | } 84 | return result 85 | } 86 | -------------------------------------------------------------------------------- /cmd/4_gogc_1000/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime/debug" 7 | "runtime/trace" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | const ( 13 | NumWorkers = 4 // Количество воркеров 14 | NumTasks = 500 // Количество задач 15 | MemoryIntense = 10000 // Размер память затратной задачи (число элементов) 16 | ) 17 | 18 | func main() { 19 | // Запись в trace файл 20 | f, _ := os.Create("trace.out") 21 | trace.Start(f) 22 | defer trace.Stop() 23 | 24 | // Установка целевого процента сборщика мусора GOGC=1000. 25 | debug.SetGCPercent(1000) 26 | 27 | // Очередь задач и очередь результата 28 | taskQueue := make(chan int, NumTasks) 29 | resultQueue := make(chan int, NumTasks) 30 | 31 | // Запуск воркеров 32 | var wg sync.WaitGroup 33 | wg.Add(NumWorkers) 34 | for i := 0; i < NumWorkers; i++ { 35 | go worker(taskQueue, resultQueue, &wg) 36 | } 37 | 38 | // Отправка задач в очередь 39 | for i := 0; i < NumTasks; i++ { 40 | taskQueue <- i 41 | } 42 | close(taskQueue) 43 | 44 | // Получение результатов из очереди 45 | go func() { 46 | wg.Wait() 47 | close(resultQueue) 48 | }() 49 | 50 | // Обработка результатов 51 | for result := range resultQueue { 52 | fmt.Println("Результат:", result) 53 | } 54 | 55 | fmt.Println("Готово!") 56 | } 57 | 58 | // Функция воркера 59 | func worker(tasks <-chan int, results chan<- int, wg *sync.WaitGroup) { 60 | defer wg.Done() 61 | 62 | for task := range tasks { 63 | result := performMemoryIntensiveTask(task) 64 | results <- result 65 | } 66 | } 67 | 68 | // performMemoryIntensiveTask функция требующая много памяти 69 | func performMemoryIntensiveTask(task int) int { 70 | // Создание среза большого размера 71 | data := make([]int, MemoryIntense) 72 | for i := 0; i < MemoryIntense; i++ { 73 | data[i] = i + task 74 | } 75 | 76 | // Имитация временной задержки 77 | time.Sleep(10 * time.Millisecond) 78 | 79 | // Вычисление результата 80 | result := 0 81 | for _, value := range data { 82 | result += value 83 | } 84 | return result 85 | } 86 | -------------------------------------------------------------------------------- /cmd/2_gogc_100/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime/debug" 7 | "runtime/trace" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | const ( 13 | NumWorkers = 4 // Количество воркеров 14 | NumTasks = 500 // Количество задач 15 | MemoryIntense = 10000 // Размер память затратной задачи (число элементов) 16 | ) 17 | 18 | func main() { 19 | // Запись в trace файл 20 | f, _ := os.Create("trace.out") 21 | trace.Start(f) 22 | defer trace.Stop() 23 | 24 | // Установка целевого процента сборщика мусора. По умолчанию GOGC=100 25 | debug.SetGCPercent(100) 26 | 27 | // Очередь задач и очередь результата 28 | taskQueue := make(chan int, NumTasks) 29 | resultQueue := make(chan int, NumTasks) 30 | 31 | // Запуск воркеров 32 | var wg sync.WaitGroup 33 | wg.Add(NumWorkers) 34 | for i := 0; i < NumWorkers; i++ { 35 | go worker(taskQueue, resultQueue, &wg) 36 | } 37 | 38 | // Отправка задач в очередь 39 | for i := 0; i < NumTasks; i++ { 40 | taskQueue <- i 41 | } 42 | close(taskQueue) 43 | 44 | // Получение результатов из очереди 45 | go func() { 46 | wg.Wait() 47 | close(resultQueue) 48 | }() 49 | 50 | // Обработка результатов 51 | for result := range resultQueue { 52 | fmt.Println("Результат:", result) 53 | } 54 | 55 | fmt.Println("Готово!") 56 | } 57 | 58 | // Функция воркера 59 | func worker(tasks <-chan int, results chan<- int, wg *sync.WaitGroup) { 60 | defer wg.Done() 61 | 62 | for task := range tasks { 63 | result := performMemoryIntensiveTask(task) 64 | results <- result 65 | } 66 | } 67 | 68 | // performMemoryIntensiveTask функция требующая много памяти 69 | func performMemoryIntensiveTask(task int) int { 70 | // Создание среза большого размера 71 | data := make([]int, MemoryIntense) 72 | for i := 0; i < MemoryIntense; i++ { 73 | data[i] = i + task 74 | } 75 | 76 | // Имитация временной задержки 77 | time.Sleep(10 * time.Millisecond) 78 | 79 | // Вычисление результата 80 | result := 0 81 | for _, value := range data { 82 | result += value 83 | } 84 | return result 85 | } 86 | -------------------------------------------------------------------------------- /cmd/6_gogc_1000_gomemlimit/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime/debug" 7 | "runtime/trace" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | const ( 13 | NumWorkers = 4 // Количество воркеров 14 | NumTasks = 500 // Количество задач 15 | MemoryIntense = 10000 // Размер память затратной задачи (число элементов) 16 | ) 17 | 18 | func main() { 19 | // Запись в trace файл 20 | f, _ := os.Create("trace.out") 21 | trace.Start(f) 22 | defer trace.Stop() 23 | 24 | // Установка целевого процента сборщика мусора GOGC=1000. 25 | debug.SetGCPercent(1000) 26 | 27 | // Очередь задач и очередь результата 28 | taskQueue := make(chan int, NumTasks) 29 | resultQueue := make(chan int, NumTasks) 30 | 31 | // Запуск воркеров 32 | var wg sync.WaitGroup 33 | wg.Add(NumWorkers) 34 | for i := 0; i < NumWorkers; i++ { 35 | go worker(taskQueue, resultQueue, &wg) 36 | } 37 | 38 | // Отправка задач в очередь 39 | for i := 0; i < NumTasks; i++ { 40 | taskQueue <- i 41 | } 42 | close(taskQueue) 43 | 44 | // Получение результатов из очереди 45 | go func() { 46 | wg.Wait() 47 | close(resultQueue) 48 | }() 49 | 50 | // Обработка результатов 51 | for result := range resultQueue { 52 | fmt.Println("Результат:", result) 53 | } 54 | 55 | fmt.Println("Готово!") 56 | } 57 | 58 | // Функция воркера 59 | func worker(tasks <-chan int, results chan<- int, wg *sync.WaitGroup) { 60 | defer wg.Done() 61 | 62 | for task := range tasks { 63 | result := performMemoryIntensiveTask(task) 64 | results <- result 65 | } 66 | } 67 | 68 | // performMemoryIntensiveTask функция требующая много памяти 69 | func performMemoryIntensiveTask(task int) int { 70 | // Создание среза большого размера 71 | data := make([]int, MemoryIntense) 72 | for i := 0; i < MemoryIntense; i++ { 73 | data[i] = i + task 74 | } 75 | 76 | // Имитация временной задержки 77 | time.Sleep(10 * time.Millisecond) 78 | 79 | // Вычисление результата 80 | result := 0 81 | for _, value := range data { 82 | result += value 83 | } 84 | return result 85 | } 86 | --------------------------------------------------------------------------------