Функция runtime.SetFinalizer

Функция runtime.SetFinalizer устанавливает функцию-финализатор для объекта. Финализатор будет вызван сборщиком мусора перед тем, как объект будет удален. В первый параметр передается указатель на объект, для которого устанавливается финализатор, во второй параметр передается функция, которая будет вызвана при финализации. Функция-финализатор должна принимать один параметр того же типа, что и объект, или типа, которому объект может быть присвоен.

Синтаксис

goruntime.SetFinalizer(obj, finalizer)

Пример

Давайте создадим простую структуру и установим для нее финализатор:

gopackage main

import (
  "fmt"
  "runtime"
)

type MyStruct struct {
  id int
}

func main() {
  obj := &MyStruct{id: 42}
  runtime.SetFinalizer(obj, func(o *MyStruct) {
    fmt.Printf("Finalizing object with id: %d\n", o.id)
  })
  runtime.GC()
}

Результат выполнения кода:

go"Finalizing object with id: 42"

В этом примере после вызова runtime.GC сборщик мусора запускается, обнаруживает, что на объект obj больше нет ссылок, и вызывает финализатор.

Пример

Покажем, как использовать финализатор для освобождения внешних ресурсов:

gopackage main

import (
  "fmt"
  "runtime"
)

type Resource struct {
  closed bool
}

func NewResource() *Resource {
  r := &Resource{closed: false}
  runtime.SetFinalizer(r, func(o *Resource) {
    if !o.closed {
      o.close()
    }
  })
  return r
}

func (r *Resource) close() {
  if !r.closed {
    r.closed = true
    fmt.Println("Resource closed")
  }
}

func main() {
  r := NewResource()
  fmt.Println("Resource created")
  runtime.GC()
}

Результат выполнения кода:

go"Resource created"
"Resource closed"

Особенности работы

Важно помнить, что финализаторы не гарантируют немедленного вызова. Они вызываются сборщиком мусора в фазе финализации, которая может произойти в любое время после того, как объект станет недостижимым. Для гарантированного вызова финализаторов можно использовать функцию runtime.GC, однако в реальных приложениях это не рекомендуется.

Финализаторы могут быть отключены вызовом runtime.SetFinalizer(obj, nil). Это полезно, если объект больше не требует финализации.

Пример

Покажем отключение финализатора:

gopackage main

import (
  "fmt"
  "runtime"
)

type Data struct {
  name string
}

func main() {
  d := &Data{name: "test"}
  runtime.SetFinalizer(d, func(o *Data) {
    fmt.Println("Finalizer called")
  })
  runtime.SetFinalizer(d, nil)
  runtime.GC()
}

Результат выполнения кода:

go 

В этом примере финализатор был отключен до того, как сборщик мусора успел его вызвать, поэтому в выводе ничего не появилось.

Важные замечания

При использовании финализаторов следует учитывать, что они могут увеличить нагрузку на сборщик мусора и замедлить выполнение программы. Финализаторы не рекомендуется использовать для освобождения памяти, так как это может привести к утечкам памяти или другим проблемам. Вместо этого следует использовать явное управление ресурсами с помощью методов Close или Dispose.

Финализаторы вызываются в отдельной горутине, поэтому они должны быть безопасны для одновременного выполнения с другими горутинами. Также стоит избегать сложных операций в финализаторах, чтобы не замедлять работу сборщика мусора.

Смотрите также

  • функцию runtime.KeepAlive,
    которая предотвращает сборку мусора до определенного момента
  • функцию runtime.GC,
    которая запускает сборку мусора вручную
  • функцию reflect.Value.IsZero,
    которая проверяет, является ли значение нулевым
  • функцию unsafe.Pointer,
    которая предоставляет доступ к указателям любого типа