Почему финализатор никогда не вызывается? - PullRequest
0 голосов
/ 22 октября 2018
var p = &sync.Pool{
    New: func() interface{} {
        return &serveconn{}
    },
}

func newServeConn() *serveconn {
    sc := p.Get().(*serveconn)
    runtime.SetFinalizer(sc, (*serveconn).finalize)
    fmt.Println(sc, "SetFinalizer")
    return sc
}

func (sc *serveconn) finalize() {
    fmt.Println(sc, "finalize")
    *sc = serveconn{}
    runtime.SetFinalizer(sc, nil)
    p.Put(sc)
}

Приведенный выше код пытается повторно использовать объект SetFinalizer, но после отладки я обнаружил, что финализатор никогда не вызывается, почему?

UPDATE

Thisможет быть связано: https://github.com/golang/go/issues/2368

1 Ответ

0 голосов
/ 22 октября 2018

Приведенный выше код пытается повторно использовать объект SetFinalizer, но после отладки я обнаружил, что финализатор никогда не вызывается, почему?

Финализатор вызывается только для объекта, когда GCпомечает его как неиспользуемый и затем пытается развернуть (освободить) в конце цикла GC.

Как следствие, если цикл GC никогда не выполняется во время выполнения вашей программы, установленные вами финализаторы могут никогдабыть вызванным.

На всякий случай, если вы ошиблись в предположении о GC Go, стоит отметить, что Go не использует подсчет ссылок на значения;вместо этого он использует GC, который работает параллельно с программой, и сеансы, во время которых он работает, происходят периодически и запускаются определенными параметрами, такими как давление на кучу, создаваемое распределениями.

Несколько различных замечаний относительно финализаторов:

  • Когда программа завершается, ГХ не запускается принудительно.

    Следствием этого является то, что финализатору вообще не гарантируется работа.

  • Если GC находит финализатор на объекте, который должен быть освобожден, он вызывает финализатор , но не освобождает объект.

    Сам объект будет освобождентолько на следующем цикле GC - потеря памяти.

В общем, вы, похоже, пытаетесь реализовать деструкторы.Пожалуйста, не надо: заставляйте ваши объекты реализовывать стандартный метод, называемый Close, и указывайте в контракте вашего типа, что программист должен вызывать его, когда они закончили с объектом.Когда программист хочет вызвать такой метод, несмотря ни на что, он использует defer.

Обратите внимание, что этот подход прекрасно работает для всех типов в stdlib Go, которые оборачивают ресурсы, предоставляемые ОС - дескрипторы файлов и сокетов,Таким образом, нет необходимости притворяться, что ваши типы чем-то отличаются.

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

...