Dieser Artikel ist der 12. Tagesartikel von freee Engineers Advent Calendar.
Hallo. Mein Name ist @taiyo und ich bin Ingenieurpraktikant mit 17 Absolventen von freee. Ich habe nur Skriptsprachen wie Ruby für Freee, PHP für Kreise und Python für Abschlussarbeiten angesprochen. Ich hatte die Möglichkeit, mich mit go in freee an der Entwicklung zu beteiligen, und fing an, sie zu berühren, und ich fing vor ungefähr 3 Monaten an, go zu mögen.
Ich bin noch ungefähr 1 Jahr und 3 Monate ein neuer Ingenieur, aber ich wurde durch die später beschriebenen Ereignisse ausgelöst. Als "** Ich möchte die Speicherprofilerstellung auf der C-Seite mit vorgefertigten Anwendungen, die cgo ** verwenden, vereinfachen" hat zugenommen.
Ich möchte über diese drei Punkte schreiben.
Dies ist der Anfang der Sache. Nach der Freigabe einer wesentlichen Änderung am Go-API-Server trat einige Tage später ein Speicherverlust auf. Ich wachte auf ...! !!
Wie bereits erwähnt, bin ich nie auf das Phänomen von Speicherverlusten gestoßen, indem ich nur Skriptsprachen berührt habe, und es war meine erste Erfahrung, daher war ich wirklich ungeduldig. Ich wusste nicht, was ich tun sollte, aber ich fuhr mit Ratschlägen fort, während ich warm war.
Zusammenfassend wurde die Ursache sowohl auf der Go-Seite als auch auf der C-Seite gefunden.
Die Anwendung enthielt also Go's Standardbibliothek net / http / pprof
Ich habe es verwendet, um die Speichernutzung zu überprüfen.
Während meiner Recherche erinnere ich mich, dass ich einfach "Wow!" Gesagt habe, um "net / http / pprof" und die Größe von go, die als Standardbibliothek implementiert ist, zu vereinfachen.
Immerhin in der Untersuchung hier ** CString von cgo, das nicht das Ziel von GC of go ist Daten, von denen ich dachte, ich könnte sie veröffentlichen Es stellte sich heraus, dass eine der Ursachen darin bestand, dass sie nicht veröffentlicht wurde **.
Code zur Freigabe hinzufügen ↓,
defer C.free(unsafe.Pointer(released))
Sofort reparieren und loslassen! Dies sollte es beheben!
... dachte ich, aber es gab immer noch ein Speicherleck.
Ich habe mit pprof gesucht und konnte keine mögliche Ursache finden. Da diese Anwendung cgo verwendet, habe ich beschlossen, den C-Code zu untersuchen und den ** C-Implementierungsteil von Go zu trennen und valgrind von ** zu setzen Bei der Untersuchung fand ich einen Teil auf der C-Seite, bei dem es sich wahrscheinlich um einen Leckagengpass handelte.
Ich habe es repariert und wieder freigegeben, und dann ist das Leck nicht aufgetreten, sodass die Situation vorerst geregelt war.
In der ersten Umfrage ist net / http / pprof
erstaunlich! !! Ich dachte, aber es gab eine unerwartete Falle.
Das Profilierungsziel von "net / http / pprof" ist nur der von go zugewiesene Speicher.
** C scheint nicht auszugeben **.
Obwohl es ein wichtiger Wert in C- und C ++ - Speicherprofilern ist, scheint es, wie oben erwähnt, von Go und cgo (https://github.com/golang/go/issues/782) bisher nicht unterstützt zu werden. Ich musste ** C-Code getrennt von Go ** untersuchen.
Dies würde separate ** go- und C-Untersuchungen ** erfordern, was einige Zeit in Anspruch nehmen würde. Stattdessen möchten Sie auf einen Schlag einen Engpass finden.
net / http / pprof
sehen zu könnenIch möchte auch eine Umgebung schaffen, in der es einfach ist zu untersuchen, wann dasselbe passiert.
net / http / pprof
Immerhin ist es praktisch, also wäre es schön, in der Lage zu sein, auszugeben, welche Zeile von C mit derselben Schnittstelle Speicher belegt.
Da es cgo gibt, sollte die Kompatibilität zwischen go und C hoch sein, also frage ich mich, ob ich etwas dagegen tun kann. Also habe ich beschlossen, verschiedene Dinge auszuprobieren.
Die folgenden drei sind Kandidaten.
Was 2 betrifft, dachte ich, dass es viel Zeit und Kraft kosten würde, also Dieses Mal konzentrierte ich mich darauf zu untersuchen, ob die Profilerstellung von pprof durch "Ausrichten von Go- und C-Allokatoren" in 1 durchgeführt werden kann.
Vorerst folgte ich dem Quellcode von net / http / pprof
.
Schließlich kamen wir zu runtime / malloc.go
runtime/malloc.go
// Memory allocator.
//
// This was originally based on tcmalloc, but has diverged quite a bit.
// http://goog-perftools.sourceforge.net/doc/tcmalloc.html
// The main allocator works in runs of pages.
// Small allocation sizes (up to and including 32 kB) are
// rounded to one of about 70 size classes, each of which
// has its own free set of objects of exactly that size.
// Any free page of memory can be split into a set of objects
// of one size class, which are then managed using a free bitmap.
//
// The allocator's data structures are:
//
// fixalloc: a free-list allocator for fixed-size off-heap objects,
// used to manage storage used by the allocator.
// mheap: the malloc heap, managed at page (8192-byte) granularity.
// mspan: a run of pages managed by the mheap.
// mcentral: collects all spans of a given size class.
// mcache: a per-P cache of mspans with free space.
// mstats: allocation statistics.
Wie Sie in diesem Kommentar sehen können, basiert ** go seine Speicherzuordnung auf tcmalloc, was in C normal ist. Ich habe Malloc benutzt. ** **. Wenn sich die für die Speicherzuweisung verwendeten Allokatoren unterscheiden und gleich sind, werden sie daher gut aufgenommen. Ich habe die Hypothese aufgestellt.
Eigentlich ursprünglich "Wie wäre es mit der Kombination der verschiedenen Allokatoren zwischen C und Go?" Ich wurde geraten. Zu dieser Zeit verstand ich es jedoch ehrlich gesagt nicht sehr gut und nachdem ich den Code bisher gelesen hatte, fühlte ich mich endlich verbunden. Testen wir vorerst die Hypothese, dass "versucht wird, den C-Allokator mit dem tcmalloc abzugleichen".
Der Ersatz durch malloc-> tcmalloc wird am häufigsten bei Suchanfragen gefunden und in der Hoffnung, dass er mit go von google gperftools kompatibel ist. Ich versuchte es. (Ich war ein wenig besorgt, dass es keinen brandneuen Artikel über gperftools gab, aber das Github-Repository-Commit war das neueste in 2016/11.)
Dieses Mal habe ich gperftools in die Version der go-Anwendung eingeführt, die den Speicherverlust verursacht hat, und den Vorgang überprüft. Die Entwicklungsumgebung wird mithilfe von Docker auf dem Container erstellt.
Unten ist der zusätzliche Code. (Vereinfacht)
#Dockerfile
...
RUN apt-get -y install google-perftools libgoogle-perftools-dev
...
cgo_sample.go
package main
/*
...
#cgo CFLAGS: -I/usr/include
#cgo LDFLAGS: -L/usr/lib -ltcmalloc
#include "gperftools/tcmalloc.h"
*/
import "C"
import (
"net/http"
_ "net/http/pprof"
);
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8800", nil)
}
func handler(w http.ResponseWriter, r *http.Request) {
//Verarbeitung, die in C usw. ausläuft
}
Installieren Sie google-perftools und libgoogole-perftools-dev mit apt-get. Die beiden von apt-get installierten befinden sich unter / usr. Geben Sie sie daher in CFLAGS bzw. LDFLAGS an.
$ curl http://localhost:8800
$ go tool pprof http://localhost:8800/debug/pprof/heap?debug=1
(pprof) text
5836.79kB of 5836.79kB total ( 100%)
Dropped 57 nodes (cum <= 29.18kB)
Showing top 10 nodes out of 23 (cum >= 512.69kB)
flat flat% sum% cum cum%
2430.03kB 41.63% 41.63% 2430.03kB 41.63% go.SomeLeakMethod1
1334.04kB 22.86% 64.49% 1334.04kB 22.86% go.OtherLeakMethod1
1048.02kB 17.96% 82.44% 1048.02kB 17.96% go.SomeLeakMethod2
512.02kB 8.77% 100% 512.02kB 8.77% go.OtherLeakMethod2
0 0% 100% 1048.02kB 17.96% go.NLeakMethod
0 0% 100% 3764.07kB 64.49% go.NLeakMethod2
Selbst wenn der Allokator auf tcmallocc ausgerichtet war, wurde nur die Go-Funktion angezeigt, die der Eingang zum Go war, und das Ergebnis war das gleiche wie das ursprüngliche pprof. Leider wird die Hypothese, dass "das Ausrichten der Allokatoren zur Ausgabe durch pprof führt", zurückgewiesen.
Es ist auch im ursprünglichen Repository von gperftools geschrieben, aber es scheint, dass gperftools unter Linux-64bit nicht gut funktioniert. Seien Sie also auch dort vorsichtig. Die Antwort war ungefähr 1,5-mal langsamer als gewöhnlich, obwohl es sich um eine Bibliothek zur Beschleunigung der Speicherzuweisung handeln sollte. .. Selbst wenn es gelingt, scheint die Installation ein wenig zu kosten.
Ersteres war das, was ich beim Lesen des Codes von malloc.go dachte. Letzteres könnte durch die Untersuchung von Prozessen und Threads entstanden sein.
In jedem Fall gab es Fortschritte bei der Verbesserung der Profilerstellung von cgo mit nur einer Möglichkeit.
Durch die Speicherverlustreaktion meiner ersten Erfahrung wurde klar, dass nicht nur die Sprachspezifikationen von go, sondern auch Informatikkenntnisse weit verbreitet sind, um mit solchen Situationen umzugehen. Dieses Mal, insbesondere für Computer, konnte ich CPU und Speicher eingeben, und für Sprachen konnte ich Sprachspezifikationen für deren Verwendung eingeben, aber gleichzeitig genoss ich die praktische Oberfläche, die von meinen Vorgängern so wie sie war erstellt wurde. Ich verstand auch, dass ich es benutzte, ohne tief nachzudenken. Es macht mir klar, dass es noch viele Dinge gibt, die ich nicht weiß, und ich fühle mich angespannt.
In der Entwicklungskultur von Freee gibt es einen Punkt "** Lass uns scheitern und angreifen **". Ich erkenne dies als "ein Fehler ist eine Wachstumsquelle für mich und ich ergreife aggressive Maßnahmen, um denselben Fehler beim nächsten Mal zu verhindern."
Für mich ist genau das, was meiner Meinung nach an diesem Speicherverlust beteiligt ist.
Wenn Sie ein Gefühl der Herausforderung haben, werden Sie sich motivierter fühlen und es tun. Wir werden diese Denkweise nicht vergessen und unsere Stärke stärken, damit wir sicher Verbesserungsmaßnahmen präsentieren können.
Ich habe diesmal 1 untersucht, aber leider keine guten Ergebnisse erzielt. Es war jedoch eine großartige Gelegenheit, den Quellcode zu lesen und zu verschieben, obwohl ich mit Go und C nicht vertraut war. Der Rest ist 2 (1) Plan, aber ich werde ein wenig Zeit damit verbringen, den Quellcode von Go zu lesen und an 2 zu arbeiten. Ich möchte den Follow-up-Bericht später aktualisieren.
Es ist das Ende, freee Co., Ltd. sucht langjähriger Ingenieurpraktikant, Ingenieur 17, 18 Absolventen, der Benutzern mit neuen Sprachen und Technologien wie Go einen ernsthaften Mehrwert bieten möchte. Ich bin. Es gibt auch ein Mittagessensystem, in dem Sie ins Büro kommen und zu Mittag essen können. Wenn Sie sich sowohl für Studenten als auch für Berufstätige interessieren, können Sie sich gerne an uns wenden.
Morgen, Dreieinhalb Jahre von der Entwicklung entfernt. Mein Lieblingsessen ist Komiyas Holzkohle. Dies ist unser CEO @ dice-sasaki @ github, der dieses Jahr wieder zu uns kommt! freue mich auf!