Функция runtime.SetFinalizer
Функция runtime.SetFinalizer устанавливает функцию-финализатор для объекта.
Финализатор будет вызван сборщиком мусора перед тем, как объект будет удален.
В первый параметр передается указатель на объект, для которого устанавливается финализатор,
во второй параметр передается функция, которая будет вызвана при финализации.
Функция-финализатор должна принимать один параметр того же типа, что и объект,
или типа, которому объект может быть присвоен.
Синтаксис
runtime.SetFinalizer(obj, finalizer)
Пример
Давайте создадим простую структуру и установим для нее финализатор:
package 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()
}
Результат выполнения кода:
"Finalizing object with id: 42"
В этом примере после вызова runtime.GC сборщик мусора запускается,
обнаруживает, что на объект obj больше нет ссылок, и вызывает финализатор.
Пример
Покажем, как использовать финализатор для освобождения внешних ресурсов:
package 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()
}
Результат выполнения кода:
"Resource created"
"Resource closed"
Особенности работы
Важно помнить, что финализаторы не гарантируют немедленного вызова.
Они вызываются сборщиком мусора в фазе финализации, которая может произойти
в любое время после того, как объект станет недостижимым.
Для гарантированного вызова финализаторов можно использовать функцию
runtime.GC, однако в реальных приложениях это не рекомендуется.
Финализаторы могут быть отключены вызовом runtime.SetFinalizer(obj, nil).
Это полезно, если объект больше не требует финализации.
Пример
Покажем отключение финализатора:
package 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()
}
Результат выполнения кода:
В этом примере финализатор был отключен до того, как сборщик мусора успел его вызвать, поэтому в выводе ничего не появилось.
Важные замечания
При использовании финализаторов следует учитывать, что они могут
увеличить нагрузку на сборщик мусора и замедлить выполнение программы.
Финализаторы не рекомендуется использовать для освобождения памяти,
так как это может привести к утечкам памяти или другим проблемам.
Вместо этого следует использовать явное управление ресурсами
с помощью методов Close или Dispose.
Финализаторы вызываются в отдельной горутине, поэтому они должны быть безопасны для одновременного выполнения с другими горутинами. Также стоит избегать сложных операций в финализаторах, чтобы не замедлять работу сборщика мусора.
Смотрите также
-
функцию
runtime.KeepAlive,
которая предотвращает сборку мусора до определенного момента -
функцию
runtime.GC,
которая запускает сборку мусора вручную -
функцию
reflect.Value.IsZero,
которая проверяет, является ли значение нулевым -
функцию
unsafe.Pointer,
которая предоставляет доступ к указателям любого типа