In Go wird ein pragmatischer Ansatz zur Ressourcenverwaltung verfolgt. Anstelle von try-finally, das aus anderen Sprachen bekannt ist, gibt es defer: einen eingebauten Mechanismus, der eine "verzögerte" Funktion aufzeichnet und sie beim Verlassen des Geltungsbereichs ausführt. Dieses Werkzeug wird häufig verwendet, um Ressourcen (Dateien, Netzwerkverbindungen) automatisch freizugeben.
Wenn man vergisst, Close für eine Datei oder Verbindung aufzurufen, kann es zu Ressourcenlecks oder Blockaden kommen — was in Server- und Datei-Anwendungen kritisch wichtig ist. Der verzögerte Aufruf mit defer stellt sicher, dass die Abschlussfunktion auch im Falle eines Fehlers oder panic aufgerufen wird. Es gibt jedoch besondere Fälle, in denen die falsche Verwendung von defer zu Fehlern führt: Der Aufruf von defer in einer Schleife, die Übergabe eines ungültigen Objekts oder die Arbeit mit einer großen Anzahl von defers kann zu einem Overhead führen.
Rufen Sie immer defer f.Close() sofort nach dem erfolgreichen Öffnen einer Ressource auf, um vergessene Schließungen zu vermeiden. Verwenden Sie defer nicht in engen Schleifen zur Beschleunigung und Einsparung von Speicher, wenn sehr viele Dateien geöffnet werden. Versuchen Sie, das Öffnen von Dateien/Ressourcen in eine Funktion zu kapseln, um den Geltungsbereich zu minimieren.
Beispielcode:
file, err := os.Open("data.txt") if err != nil { log.Fatal(err) } defer file.Close() // garantiertes Schließen // ... Arbeiten mit der Datei
Schlüsselmerkmale:
Wann wird defer ausgeführt und wann werden seine Parameter berechnet?
Die Parameter der Funktion in defer werden sofort zum Zeitpunkt der Deklaration von defer berechnet, nicht beim Ausführen von defer.
Beispielcode:
func main() { a := 1 defer fmt.Println(a) // merkt sich 1 a = 42 } // gibt 1 aus
Kann defer zu Speicherlecks oder Verlangsamungen führen?
Ja, wenn man defer in einer Schleife verwendet, in der viele Dateien oder Objekte geöffnet werden, wird jede solche defer im Stapel der verzögerten Aufrufe gespeichert, der nur beim Verlassen der Funktion gereinigt wird, was zu einem unnötigen Anstieg des Speichers führt.
Beispielcode:
for i := 0; i < 10000; i++ { f, _ := os.Open("file.txt") defer f.Close() // hält alle 10000 Dateien bis zum Ende von main geöffnet }
Was passiert, wenn der Aufruf von f.Close() einen Fehler zurückgibt und dieser "geschluckt" wird?
Die Standardpraxis ist, den Fehler beim Schließen von Ressourcen zu protokollieren. Wenn dieser Punkt ignoriert wird, können Fehlfunktionen oder partielle Dateiaufzeichnungen übersehen werden, z.B. beim Nichtlöschen von temporären Dateien oder bei Netzwerkfehlern.
In einer Schleife zur Verarbeitung von Dateien platziert der Entwickler defer f.Close() für jede Datei. Dadurch sind gleichzeitig zehntausende von Dateien geöffnet, die Ausführung des Programms verlangsamt sich und es gehen dem System die Dateideskriptoren aus.
Vorteile:
Nachteile:
In der Schleife wird die Verarbeitung jeder Datei in einer separaten Funktion ausgeführt, in der defer f.Close() nur einmal pro Verarbeitung aufgerufen wird und sofort die Ressource freigibt.
Vorteile:
Nachteile: