问题
The following Open
followed by a deferred Close
is idiomatic in Go:
func example() {
f, err := os.Open(path)
if err != nil {
return
}
defer f.Close()
}
What happens if I don't have defer f.Close()
?
When I call this function and f
goes out of scope, does it automatically close the file or do I have a zombie file handle?
If it closes automatically, when exactly does it do this?
回答1:
It is true Files are closed when garbage collected, but... as mentioned in "Mystery of finalizers in Go" from Alexander Morozov -- LK4D4math:
In Go we have both GC and pro-users :)
So, in my opinion explicit call ofClose
is always better than magic finalizer.
Alexander adds:
The problem with finalizers is that you have no control over them, and, more than that, you’re not expecting them.
Look at this code:
func getFd(path string) (int, error) {
f, err := os.Open(path)
if err != nil {
return -1, err
}
return f.Fd(), nil
}
It’s pretty common operation to get file descriptor from path when you’re writing some stuff for linux.
But that code is unreliable, because when you’re return fromgetFd()
,f
loses its last reference and so your file is doomed to be closed sooner or later (when next GC cycle will come).Here, the problem is not that file will be closed, but that it is not documented and not expected at all.
There was a proposal to extend the finalizer and detecting leaks (like file descriptor leaks)
But... Russ Cox quashed that down convincingly:
Anyone interested in this topic should read Hans Boehm's paper "Destructors, Finalizers, and Synchronization".
It greatly influenced our decision to limit the scope of finalizers in Go as much as possible.
They are a necessary evil for allowing reclamation of non-(heap memory) resources at the same time as associated heap memory, but they are inherently much more limited in their capabilities than most people initially believe.We will not be expanding the scope of finalizers, either in implementation or in the standard library, or in the x repos, nor will we encourage others to expand that scope.
If you want to track manually-managed objects, you'd be far better off using runtime/pprof.NewProfile.
For example, inside Google's source tree we have a Google-wide "file" abstraction, and the Go wrapper package for this declares a global:
var profiles = pprof.NewProfile("file")
The end of the function that creates a new File says:
profiles.Add(f, 2)
return f
and then
f.Close
does
profiles.Remove(f)
Then we can get a profile of all in-use files, "leaked" or otherwise, from
/debug/pprof/file
or frompprof.Lookup("file").WriteTo(w, 0)
.
And that profile includes stack traces.
回答2:
Files will be closed automatically when os.File is garbage collected. This appears to be done by SetFinalizer calls so the file will be closed eventually, not immediately after it becomes unreachable.
回答3:
Go applications are expected to explicitly release resources. There is no language feature that will implicitly release a resource when a variable goes out of scope. The language does provide the defer feature to make it easier to write the explicit code.
As bserdar and VonC noted, the garbage collector has a hook for releasing external resources (see runtime.SetFinalizer). This hook is used by the os.File type. Applications should not rely on this hook because it's not specified when objects are collected, if at all.
来源:https://stackoverflow.com/questions/58351084/does-go-automatically-close-resources-if-not-explicitly-closed