Go 1.20 and the memory arena

One of the revolutionary features of Go compared to other compiled languages ​​was the automatic management of the release of memory from unused objects (garbage collection). At the same time, it can lead to a performance penalty when transferring control to the memory management process, but an alternative mechanism has not been provided in Go. Starting with Go 1.20, support for an experimental memory management solution that allows you to combine safe dynamic memory allocation and reduce the impact of memory management integrated into compiled code on application performance. In this article, we will cover the main aspects of using Memory Arena in Go 1.20.

To run the code, we will use the current version of Go 1.20rc2, which can be obtained from the installation package or via go install golang.org/dl/go1.20rc2@latest

To enable support for the new memory management mechanism, add an environment variable:

export GOEXPERIMENT=arenas

now we will use the new arena module to allocate memory:

package main

import "arena"

type Person struct{
  Lastname string
  Firstname string
}

func main() {
  mem := arena.NewArena()
  defer mem.Free()

  for i:=0; i<10; i++ {
    obj := arena.New[Person](mem)
    print(obj)
  }
}

As you can see at startup, object addresses will be allocated sequentially from a single memory area and (after calling free) the entire allocated arena will be freed. When used correctly, this improves the performance of the code, because automatic garbage collection will not be called for the arena. If it is necessary to copy data to a regular heap, the Clone method can be used, which creates a copy of the structure from the arena to regular dynamic memory (for example, if it is necessary to return the result of processing to the main application). Also in the arena, you can create slices with an indication of the initial size and potential capacity arena.MakeSlice(mem, initial, capacity).

To allocate memory based on type from reflect you can also use the new method reflect.ArenaNew(mem, typ)which returns a pointer to an object of the given type allocated in the arena stored in mem.

You can detect errors when using the arena (for example, reading or writing a value to a structure after the arena is freed) by mechanisms go run -asan (Address Sanitizer) or go run -msan (Memory Sanitizer), for example:

package main

import "arena"

type Data struct {
  value int32
}

func main() {
  mem := arena.NewArena()
  v := arena.New[Data](mem)
  mem.Free()
  v.value = 1
}

when run with asan/msan it will show an invalid pointer usage error after the arena is freed.

To store strings in the arena, you can use the creation of a memory area from a sequence of bytes and copy the contents of the string into it, like this:

src := "original"

mem := arena.NewArena()
defer mem.Free()

bs := arena.MakeSlice[byte](mem, len(src), len(src))
copy(bs, src)
str := unsafe.String(&bs[0], len(bs))

The arena can also be used not only to store structures, but also for primitive data types (or their sequences), in which case the interaction is no different from working with a pointer to a variable:

package main

import "arena"

func main() {
  mem := arena.NewArena()
  defer mem.Free()
  v := arena.New[int32](mem)
  *v = 10
  println(*v)
}

Likewise, slices in an arena behave the same as normal slices in Go:

package main

import "arena"

func main() {
  mem := arena.NewArena()
  defer mem.Free()
  v := arena.MakeSlice[int32](mem,50,100)
  v[49] = 10;
  v = append(v, 20)
  println(v[49])		//10
  println(v[50])		//20
  println(len(v))		//51
  println(cap(v))		//100
}

To detect memory leaks when using the arena, you can use the usual profiling mechanisms in Go (go tool pprof to visualize sampling of memory allocation, which can be stored through the functions of the runtime/pprof module). From the point of view of memory allocation, working with an arena is similar to allocating a single block of memory (which can grow in size) and when the arena is released, all objects allocated in it become inaccessible.

Performance improvements can be expected in cases where the application allocates memory heavily (for example, when storing binary trees or other related data structures), but it is assumed that the allocated data structures are long-lived and exist until the entire arena is freed (the garbage collector for the arena is not is applied and allocated objects in the sequential memory area are not cleared).

The article was prepared in anticipation of the start of the course golang developer. professional. I invite everyone to a free webinar, where the course leader will interview the graduate of the program. Real questions, comments on answers, advice. It will be interesting.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *