Это правда, что файлы закрываются при сборке мусора, но ... как уже упоминалось в " Тайна финализаторов в Go " от Александр Морозов - LK4D4math :
В Go у нас есть и GC, и пользователи:)
Так что, на мой взгляд, явный вызов Close
всегда лучше, чем магический финализатор .
Александр добавляет:
Проблема с финализаторами заключается в том, что вы не можете их контролировать, и, более того, вы их не ожидаете.
Посмотрите на этот код:
func getFd(path string) (int, error) {
f, err := os.Open(path)
if err != nil {
return -1, err
}
return f.Fd(), nil
}
Это довольно распространенная операция, чтобы получить дескриптор файла по пути, когда вы пишете что-то для linux.
Ноэтот код ненадежен, потому что когда вы возвращаетесь из getFd()
, f
теряет свою последнюю ссылку, и поэтому ваш файл обречен на закрытие рано или поздно (когда наступит следующий цикл GC).
Здесь проблема заключается в не в том, что файл будет закрыт, но в том, что он не задокументирован и вообще не ожидается.
Былпредложение расширить финализатор и обнаружить утечки (например, утечки дескриптора файла)
Но ... Russ Cox убедительно отменило это:
Любой, кто интересуется этой темой, должен прочитать статью Ханса Бома " Деструкторы, финализаторы и синхронизация ".
Это сильно повлияло на наше решение ограничить область применения финализаторов в Go. насколько это возможно.
Они представляют собой необходимое зло для того, чтобы разрешить восстановление ресурсов, не относящихся к куче, в то же время, что и кучи памяти, но по своей природе они гораздо более ограничены в своих возможностях, чем первоначально полагает большинство людей.
Мы не будем расширять область видимости финализаторов, ни в реализации, ни в стандартной библиотеке, ни в репозиториях x, ни поощрять других расширять эту область.
Если вы хотитедля отслеживания объектов, управляемых вручную, вам будет гораздо лучше использовать runtime/pprof.NewProfile
.
Например, внутри дерева исходных текстов Google у нас есть абстракция «file» для всего Google, и пакет оболочки Go для этого объявляет глобальный:
var profiles = pprof.NewProfile("file")
Конец функции, которая создает новый файл, говорит:
profiles.Add(f, 2)
return f
, а затем f.Close
делает
profiles.Remove(f)
Тогда мыможет получить профиль всех используемых файлов, «утечка» или иным образом, от /debug/pprof/file
или от pprof.Lookup("file").WriteTo(w, 0)
.
И этот профиль включает в себя трассировки стека.