Go автоматически закрывает ресурсы, если они явно не закрыты? - PullRequest
1 голос
/ 12 октября 2019

Следующее Open, за которым следует отложенный Close, является идиоматическим в Go:

func example() {
    f, err := os.Open(path)
    if err != nil {
        return
    }
    defer f.Close()
}

Что произойдет, если у меня нет defer f.Close()?

Когда я вызываю эту функцию и f выходит из области видимости, автоматически закрывается ли файл или у меня есть дескриптор файла зомби?

Если он закрывается автоматически, когда именно он это делает?

Ответы [ 3 ]

6 голосов
/ 12 октября 2019

Это правда, что файлы закрываются при сборке мусора, но ... как уже упоминалось в " Тайна финализаторов в 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).
И этот профиль включает в себя трассировки стека.

1 голос
/ 12 октября 2019

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

Как отметили bserdar и VonC, сборщик мусора имеет ловушку для освобождения внешних ресурсов (см. runtime.SetFinalizer ). Этот хук используется типом os.File. Приложения не должны полагаться на этот хук, потому что он не указан, когда объекты собираются, если вообще.

1 голос
/ 12 октября 2019

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...