Hogyan vizsgáltam a Go memóriaszivárgásait a pprof használatával egy nagy kódalapon

Az év nagyobb részében a Go-val dolgoztam, méretezhető blokklánc-infrastruktúrát valósítottam meg az Orbs-nál, és ez egy izgalmas év volt. 2018 folyamán kutattuk, hogy melyik nyelvet válasszuk a blokklánc-megvalósításhoz. Ez arra késztetett minket, hogy a Go mellett döntsünk, mert megértettük, hogy jó közösséggel és csodálatos eszközkészlettel rendelkezik.

Az elmúlt hetekben rendszerünk integrációjának utolsó szakaszába lépünk. Mint minden nagy rendszerben, előfordulhatnak olyan későbbi szakaszbeli problémák is, amelyek teljesítményproblémákat foglalnak magukban, bizonyos memóriaszivárgások esetén. A rendszer integrálásakor rájöttünk, hogy találtunk egyet. Ebben a cikkben kitérek a memóriaszivárgás kivizsgálásának sajátosságaira a Go-ban, részletesen ismertetve a megtalálásához, megértéséhez és megoldásához szükséges lépéseket.

A Golang által kínált eszközkészlet kivételes, de korlátai vannak. Ezeket először érintve a legnagyobb a teljes mag szemétlerakódásának kivizsgálásának korlátozott képessége. A teljes mag kiírása a memória (vagy a felhasználói memória) képe lesz, amelyet a programot futtató folyamat készített.

Elképzelhetjük a memória feltérképezését faként, és ennek a fának a bejárása végigvezet minket az objektumok és a kapcsolatok különböző allokációin. Ez azt jelenti, hogy bármi is van a gyökérzetben, az oka a memória „tárolásának”, és nem a GCingelésnek (szemétgyűjtés). Mivel a Go-ban nincs egyszerű módszer a teljes magdump elemzésére, nehéz eljutni egy olyan objektum gyökeréhez, amely nem kap GC-ed-et.

Az írás idején nem találtunk olyan eszközt az interneten, amely ebben segíthetne. Mivel létezik egy alapvető dump formátum és elég egyszerű módja annak exportálására a hibakereső csomagból, előfordulhat, hogy van ilyen a Google-nál. Az online keresés úgy tűnik, mintha a Golang-vezetékben lenne, létrehozva egy ilyen magnézium-nézőt, de nem úgy néz ki, mintha bárki is dolgozna rajta. Ennek ellenére, még egy ilyen megoldáshoz való hozzáférés nélkül is, a meglévő eszközökkel általában eljuthatunk a kiváltó okig.

Memória szivárog

A memória szivárgása vagy a memória nyomása a rendszer egészében sokféle formában jelentkezhet. Általában hibaként kezeljük őket, de néha kiváltó okuk a tervezési döntésekben lehet.

Miközben a kialakulóban lévő tervezési elvek alapján építjük fel rendszerünket, az ilyen szempontok nem gondolhatók fontosnak, és ez rendben is van. Fontosabb, hogy a rendszert úgy építsük fel, hogy elkerüljük az idő előtti optimalizálást, és lehetővé tegyük azok későbbi végrehajtását a kód lejáratakor, ahelyett, hogy túlterveznénk a kezdetektől. Mégis, néhány gyakori példa a memória nyomásával kapcsolatos problémák megvalósulására:

  • Túl sok allokáció, helytelen adatábrázolás
  • A reflexió vagy a húrok erős használata
  • Globálisok használata
  • Árva, soha véget nem érő gorut

A Go alkalmazásban a memóriaszivárgás létrehozásának legegyszerűbb módja egy globális változó, tömb meghatározása és adatok hozzáfűzése ehhez a tömbhöz. Ez a nagyszerű blogbejegyzés jó értelemben írja le ezt az esetet.

Akkor miért írom ezt a bejegyzést? Amikor ezt az esetet kutattam, sok forrást találtam a memóriaszivárgásokról. Mégis, a valóságban a rendszerek több mint 50 kódsorral és egyetlen struktúrával rendelkeznek. Ilyen esetekben a memóriaprobléma forrásának megkeresése sokkal összetettebb, mint amit az adott példa leír.

Golang egy csodálatos eszközt ad nekünk pprof. Ez az eszköz elsajátítva segítséget nyújthat a memóriaproblémák kivizsgálásában és valószínűleg megtalálásában. Egy másik célja a CPU-problémák kivizsgálása, de ebben a bejegyzésben nem térek ki a CPU-val kapcsolatos dolgokra.

go eszköz pprof

Minden, amit ez az eszköz végez, több blogbejegyzésre is szükség lesz. Az egyik dolog eltartott egy ideig, hogy megtudja, hogyan lehet ezt az eszközt használni ahhoz, hogy valami működőképes legyen. Ezt a bejegyzést a memóriával kapcsolatos jellemzőire fogom koncentrálni.

A pprofcsomag egy halom mintavételű dump fájlt hoz létre, amelyet később elemezhet és vizualizálhat, hogy térképet kapjon mindkettőről:

  • Aktuális memória-allokációk
  • Összes (kumulatív) memóriaallokáció

Az eszköz képes összehasonlítani a pillanatképeket. Ez lehetővé teszi például a most és 30 másodperccel ezelőtt történtek különbségének összehasonlítását. Stresszhelyzeteknél ez hasznos lehet a kód problémás területeinek felkutatásában.

pprof profilok

A pprof működésének módja a profilok használata.

A profil a verem nyomkövetések gyűjteménye, amely bemutatja azokat a hívássorozatokat, amelyek egy adott esemény, például allokáció példányaihoz vezettek.

A runtime / pprof / pprof.go fájl részletes információkat és a profilok megvalósítását tartalmazza.

A Go több beépített profillal rendelkezik, amelyek felhasználhatók általános esetekben:

  • goroutine - az összes aktuális gorut nyomainak verem
  • halom - az élő objektumok memória-allokációinak mintavétele
  • allocs - az összes korábbi memóriaallokáció mintavétele
  • threadcreate - verem nyomok, amelyek új operációs rendszer szálak létrehozásához vezettek
  • blokk - verem nyomok, amelyek blokkoláshoz vezettek a szinkronizációs primitíveken
  • mutex - a vitatott mutexek birtokosainak verem nyomai

A memóriaproblémák vizsgálatakor a kupacprofilra fogunk koncentrálni. Az allocs profil megegyezik az általa végzett adatgyűjtéssel. A kettő közötti különbség az, ahogyan a pprof eszköz ott olvassa a kezdési időpontban. Az Allocs profil a pprof-ot egy olyan módban indítja el, amely megjeleníti a program kezdete óta kiosztott bájtok teljes számát (beleértve a szemétszedett bájtokat is). Általában ezt a módot fogjuk használni, amikor megpróbáljuk hatékonyabbá tenni a kódunkat.

A kupac

Absztrakt módon itt az operációs rendszer (Operációs Rendszer) tárolja a kódunk által használt objektumok memóriáját. Ez az a memória, amely később „szemetet gyűjt”, vagy manuálisan szabadul fel a nem szeméttől összegyűjtött nyelveken.

A kupac nem az egyetlen hely, ahol memóriafoglalások történnek, némi memória is fel van osztva a Veremben. A Stack célja rövid távú. A Go-ban a verem általában olyan feladatokhoz használható, amelyek egy függvény bezárásakor történnek. Egy másik hely, ahol a Go használja a veremet, amikor a fordító „tudja”, hogy mennyi memóriát kell lefoglalni a futási idő előtt (pl. Fix méretű tömbök). Van egy mód a Go fordító futtatására, így az elemzést ad ki arról, hogy a kiosztások hol menekülnek el a veremből a halomba, de ebben a hozzászólásban ezt nem érintem meg.

Míg a kupacadatokat „felszabadítani” és gc-ed-íteni kell, a veremadatokat nem. Ez azt jelenti, hogy sokkal hatékonyabb a verem használata, ahol csak lehetséges.

Ez a különböző helyek kivonata, ahol a memória elosztása történik. Sokkal többről van szó, de ez kívül esik ennek a bejegyzésnek a körén.

Halomadatok megszerzése a pprof segítségével

Az eszköz megszerzéséhez két fő módszer áll rendelkezésre. Az első általában egy teszt vagy egy fióktelep része lesz, és magában foglalja az importálást runtime/pprof, majd pprof.WriteHeapProfile(some_file)a kupacinformációk megírásához hívást .

Ne feledje, hogy WriteHeapProfileez a szintaktikus cukor a futáshoz:

// lookup takes a profile namepprof.Lookup("heap").WriteTo(some_file, 0)

A dokumentumok szerint WriteHeapProfilelétezik a visszafelé kompatibilitás érdekében. A többi profilnak nincsenek ilyen parancsikonjai, és Lookup()a profiladatok megszerzéséhez a funkciót kell használnia .

A második, ami az érdekesebb, a HTTP (web alapú végpontok) használatának engedélyezése. Ez lehetővé teszi az adatok adhoc kivonását az e2e / tesztkörnyezetben futó tárolóból, vagy akár a „termelésből”. Ez még egy hely, ahol a Go futási idő és az eszközkészlet kiemelkedő. A teljes csomagdokumentáció itt található, de a TL; DR szerint hozzá kell adnia a kódjához:

import ( "net/http" _ "net/http/pprof")
...
func main() { ... http.ListenAndServe("localhost:8080", nil)}

Az importálás „mellékhatása” net/http/pprofa pprof végpontok regisztrálása a webszerver gyökér alatt /debug/pprof. Most a curl használatával megszerezhetjük a halom információs fájlokat, amelyek megvizsgálhatók:

curl -sK -v //localhost:8080/debug/pprof/heap > heap.out

A http.ListenAndServe()fentiek hozzáadása csak akkor szükséges, ha a programjában korábban nem volt http-figyelő. Ha van ilyen, akkor megragadja, és nem kell újra hallgatni. Vannak olyan módszerek is, amelyek segítségével beállíthatja azt, ServeMux.HandleFunc()amely értelmesebb lenne egy bonyolultabb http-kompatibilis program számára.

A pprof használata

Összegyűjtöttük az adatokat, mi lesz most? Mint fent említettük, két fő memóriaelemzési stratégia létezik a pprof-tal. Az egyik a jelenlegi allokációk (byte vagy objektumszám) megtekintése, az úgynevezett inuse. A másik az összes lefoglalt bájtot vagy objektumszámot vizsgálja a program futási ideje alatt, az ún alloc. Ez azt jelenti, hogy függetlenül attól, hogy gc-ed volt-e, a mintának összesített összegzése.

Ez egy jó hely annak megismételésére, hogy a kupac profil a memóriaallokációk mintavétele . pprofa színfalak mögött használja a runtime.MemProfilefunkciót, amely alapértelmezés szerint összegyűjti az allokációs információkat az allokált 512 KB-os bájtokról. Lehetőség van a MemProfile megváltoztatására, hogy információkat gyűjtsön az összes objektumról. Ne feledje, hogy ez valószínűleg lelassítja az alkalmazását.

Ez azt jelenti, hogy alapértelmezés szerint van némi esély arra, hogy kisebb objektumokkal is probléma léphet fel, amelyek a pprof radara alá csúsznak. Egy nagy kódbázis / hosszú futó program esetében ez nem kérdés.

Miután összegyűjtöttük a profilfájlt, ideje betölteni az interaktív pprof konzolos konzolba. Tegye ezt futtatással:

> go tool pprof heap.out

Figyeljük meg a megjelenített információkat

Type: inuse_spaceTime: Jan 22, 2019 at 1:08pm (IST)Entering interactive mode (type "help" for commands, "o" for options)(pprof)

Fontos megjegyezni itt a Type: inuse_space. Ez azt jelenti, hogy egy adott pillanat kiosztási adatait nézzük (amikor elfogtuk a profilt). A típus a konfigurációs értéke sample_index, és a lehetséges értékek a következők:

  • inuse_space - lefoglalt és még nem szabadított memória mennyisége
  • inuse_object s— a kiosztott és még nem kiadott objektumok mennyisége
  • allokálási terület - a lefoglalt memória teljes mennyisége (függetlenül a felszabadítottól)
  • allokációs objektumok - a lefoglalt objektumok teljes mennyisége (függetlenül a kiadottaktól)

Most írja topbe az interaktívat, a kimenet lesz a legfontosabb memóriafogyasztó

Láthatunk egy sort, amely mesél nekünk Dropped Nodes, ez azt jelenti, hogy kiszűrik őket. A csomópont egy objektumbejegyzés vagy egy „csomópont” a fában. A csomópontok eldobása jó ötlet a zaj csökkentésére, de néha elrejtheti a memória problémájának kiváltó okát. Erre egy példát fogunk látni, miközben folytatjuk a vizsgálatot.

Ha a profil összes adatát fel akarja venni, adja hozzá az -nodefraction=0opciót a pprof futtatásakor, vagy írja nodefraction=0be az interaktívat.

A kiadott listában két értéket láthatunk, flatés cum.

  • lapos azt jelenti, hogy az e funkció által lefoglalt memória, amelyet ez a funkció tárol
  • A cum azt jelenti, hogy a memóriát ez a funkció vagy függvény osztotta ki, amelyet a verem lehívott

Önmagában ez az információ néha segíthet megérteni, ha probléma adódik. Vegyünk például egy olyan esetet, amikor egy függvény felelős a sok memória lefoglalásáért, de nem tartja meg. Ez azt jelentené, hogy valamilyen más objektum rámutat arra a memóriára, és fenntartja azt allokálva, vagyis lehet, hogy rendszertervezési problémánk vagy hibánk van.

topAz interaktív ablak másik nagyszerű trükkje , hogy valójában fut top10. A legfelső parancs támogatja a topNformátumot, ahol Na megtekinteni kívánt bejegyzések száma található. A fent beillesztett esetben top70például a gépelés az összes csomópontot kiadja.

Vizualizációk

Míg a topNszöveges listát ad, a pprof-hoz számos nagyon hasznos megjelenítési lehetőség tartozik. Lehetőség van gépelni pngvagy gifés még sok mást (lásd go tool pprof -helpa teljes listát).

Rendszerünkben az alapértelmezett vizuális kimenet a következőképpen néz ki:

Ez elsőre megfélemlítő lehet, de ez a memória-allokációs folyamatok megjelenítése (a veremnyomok szerint) egy programban. A grafikon elolvasása nem olyan bonyolult, mint amilyennek látszik. Számmal ellátott fehér négyzet mutatja a kiosztott helyet (és a grafikon szélén az összes memória mennyiségének összesítését), és minden szélesebb téglalap az allokációs függvényt mutatja.

Ne feledje, hogy a fenti képen kivettem egy inuse_spacevégrehajtási módból egy png-t . Sokszor érdemes egy pillantást vetnie rá inuse_objectsis, mivel ez segíthet az allokációs problémák megtalálásában.

Mélyebbre ásni, megtalálni a kiváltó okot

Eddig megértettük, hogy mi foglalja le a memóriát az alkalmazásunkban futás közben. Ez segít abban, hogy megérezzük, hogyan viselkedik (vagy rosszul viselkedik) programunk.

Esetünkben láthattuk, hogy a memóriát megőrzi membuffersaz adatszerializációs könyvtár. Ez nem azt jelenti, hogy memóriaszivárgás van az adott kódszegmensben, hanem azt, hogy a memóriát ez a függvény megtartja. Fontos megérteni a grafikon és általában a pprof kimenet olvasását. Ebben az esetben, ha megértjük, hogy amikor sorosítjuk az adatokat, vagyis memóriát osztunk ki a struktúráknak és a primitív objektumoknak (int, string), soha nem szabadul fel.

Következtetésekre ugrva vagy a grafikont tévesen értelmezve feltételezhettük, hogy a szerializálás útján az egyik csomópont felelős a memória megőrzéséért, például:

Valahol a láncban láthatjuk a naplózási könyvtárunkat, amely> 50 MB lefoglalt memóriaért felel. Ez a memória, amelyet a naplózónk által meghívott függvények osztanak ki. Végiggondolva ez valójában várható. A naplózó memória-allokációkat okoz, mivel sorosítani kell az adatokat a naplóba való kimenethez, és ezáltal memória-allokációkat okoz a folyamatban.

Azt is láthatjuk, hogy a lefoglalási útvonalon a memóriát csak a sorosítás tartja meg, semmi más. Ezenkívül a naplózó által megtartott memória mennyisége a teljes memória körülbelül 30% -a. A fentiekből kiderül, hogy valószínűleg nem a naplózóval van a probléma. Ha 100% -os volt, vagy valami közeli, akkor ott kellett volna keresnünk - de nem az. Ez azt jelentheti, hogy valami olyan naplózásra kerül, amelyet nem szabad, de ez nem a memória szivárgása a naplózó által.

Ez egy jó alkalom, hogy bevezessünk egy másik pprofparancsot list. Elfogad egy reguláris kifejezést, amely szűrő lesz a felsorolásra. A „lista” valójában az allokációhoz kapcsolódó kommentált forráskód. Az általunk vizsgált naplózóval összefüggésben úgy fogunk végrehajtani list RequestNew, ahogy szeretnénk, ha látnánk a naplózóhoz intézett hívásokat. Ezek a hívások két olyan funkcióból származnak, amelyek történetesen ugyanazzal az előtaggal kezdődnek.

Láthatjuk, hogy az elvégzett allokációk az cumoszlopban ülnek , vagyis a lefoglalt memória megmarad a hívásveremben. Ez összefügg azzal, amit a grafikon is mutat. Ezen a ponton könnyen belátható, hogy az adatgyűjtő azért osztotta ki a memóriát, mert az egész „blokk” objektumot elküldtük neki. Legalább annak egyes részeit sorosítani kellett (objektumaink membuffer objektumok, amelyek mindig végrehajtanak valamilyen String()funkciót). Hasznos naplóüzenet vagy jó gyakorlat? Valószínűleg nem, de ez nem memóriaszivárgás, nem a naplózó végén vagy a naplózót hívó kódon.

listmegtalálja a forráskódot, amikor a GOPATHkörnyezetében keresi . Abban az esetben, ha a keresett gyökér nem egyezik, ami a build gépétől függ, használhatja az -trim_pathopciót. Ez segítséget nyújt a javításhoz és a kommentált forráskód megtekintéséhez. Ne felejtse el beállítani a gitjét a megfelelő elkötelezettségre, amely a kupacprofil rögzítésekor futott.

Tehát miért marad meg az emlékezet?

A vizsgálat hátterében a gyanú merült fel, hogy problémánk van - memóriaszivárgás. Erre a felfogásra jutottunk, mivel láttuk, hogy a memóriafogyasztás nagyobb, mint amire számíthatunk a rendszernek. Ráadásul azt láttuk, hogy folyamatosan növekszik, ami egy másik erős mutatója annak, hogy „itt van egy probléma”.

Ezen a ponton a Java vagy .Net esetében megnyitunk néhány „gc root” elemzést vagy profilozót, és eljutunk a tényleges objektumra, amely hivatkozik az adatokra, és létrehozza a szivárgást. Mint kifejtettük, ez nem teljesen lehetséges a Go-val, mind szerszámprobléma miatt, mind a Go alacsony szintű memória-reprezentációja miatt.

Anélkül, hogy a részletekbe bocsátkoznánk, nem gondoljuk, hogy a Go megtartja, hogy melyik objektum melyik címen van tárolva (kivéve talán a mutatókat). Ez azt jelenti, hogy valójában annak megértése, hogy melyik memóriacím képviseli az objektum (struktúra) egyik tagját, valamilyen leképezést igényel a kupacprofil kimenetéhez. Ha elméletet beszélünk, ez azt jelentheti, hogy a teljes mag kiírása előtt egy kupac profilt is el kell készítenie, hogy a címek hozzárendelhetők legyenek az allokáló sorhoz és fájlhoz, és ezáltal a memóriában ábrázolt objektumhoz.

Ezen a ponton, mivel ismerjük a rendszerünket, könnyű volt megérteni, hogy ez már nem hiba. Tervezés szerint (majdnem). De folytassuk annak feltárását, hogyan szerezhetünk információkat az eszközökből (pprof) a kiváltó ok megtalálásához.

A beállítás során nodefraction=0megnézzük a kiosztott objektumok teljes térképét, beleértve a kisebbeket is. Nézzük meg a kimenetet:

Két új alfánk van. Ismételten emlékeztetve: a pprof kupac profil a memória-allokációk mintavétele. Működő rendszerünk számára - nem hiányoznak fontos információk. A hosszabb új zöld színű fa, amely teljesen leválik a rendszer többi részéről, a tesztfutó, nem érdekes.

A rövidebb, kék színű, amelynek éle összeköti az egész rendszerrel inMemoryBlockPersistance. Ez a név magyarázza azt a „szivárgást” is, amelyet elképzeltünk. Ez az adat háttérrendszer, amely minden adatot tárol a memóriában, és nem marad fenn lemezen. Nagyon jó megjegyezni, hogy azonnal láthattuk, hogy két nagy tárgyat tart. Miért kettő? Mivel láthatjuk, hogy az objektum mérete 1,28 MB, a függvény pedig 2,57 MB-ot megtart, vagyis kettőt jelent.

A probléma ezen a ponton jól érthető. Használhatnánk a delve-t (a hibakeresőt), hogy lássuk, ez a tömb tárolja az összes blokkot a memóriánkban lévő állandó meghajtóhoz.

Tehát mit tudnánk kijavítani?

Nos, ez megszívta, emberi hiba volt. Amíg a folyamat oktatott (és a megosztás gondoskodott), nem lettünk jobbak, vagy nem?

Volt egy dolog, ami még mindig „szagolt” ezen a halom információn. A deszerializált adatok túl sok memóriát foglaltak el, miért éppen 142 MB egy olyan dologért, ami lényegesen kevesebbet kellene? . . A pprof erre válaszolhat - valójában létezik, hogy pontosan megválaszolja az ilyen kérdéseket.

A függvény kommentált forráskódjának megtekintéséhez futtatni fogjuk list lazy. Használjuk lazy, mivel a keresett függvénynév az, lazyCalcOffsets()és nem tudjuk, hogy a kódunkban egyetlen más függvény sem kezdődne lustával. A gépelés list lazyCalcOffsetstermészetesen működne is.

Két érdekes információt láthatunk. Ismét ne felejtsük el, hogy a pprof kupacprofil mintákat tartalmaz az allokációkról. Láthatjuk, hogy a flatés a cumszám egyaránt megegyezik. Ez azt jelzi, hogy a lefoglalt memóriát ezek a felosztási pontok is megtartják.

Ezután láthatjuk, hogy a make () igényel némi memóriát. Ennek van értelme, ez az adatstruktúra mutatója. Mégis azt is látjuk, hogy a 43. sorban lévő hozzárendelés memóriát vesz fel, vagyis hozzárendelést hoz létre.

Ez a térképekről tanult meg minket, ahol a hozzárendelés egy térképhez nem egyenesen változó hozzárendelés. Ez a cikk nagyon részletesen bemutatja a térkép működését. Röviden összefoglalva, egy térképen van egy rezsi, és minél több elem van, annál nagyobb lesz ez a rezsi, ha egy szelethez képest „költségbe kerül”.

A következőket kell venni egy szem só mellett: Nem lenne jó azt mondani, hogy az a használatát map[int]T, ha az adatok nem szűkösek vagy szekvenciális indexekké alakíthatók, általában szelet megvalósítással kell megkísérelni, ha a memóriafogyasztás releváns szempont . Mégis, egy nagy szelet kibővítve lelassíthatja egy műveletet, ahol a térképen ez a lassulás elhanyagolható lesz. Az optimalizáláshoz nincs varázslat.

A fenti kódban, miután ellenőriztük, hogyan használtuk azt a térképet, rájöttünk, hogy bár ritka tömbnek képzeltük, nem annyira ritkának tűnt. Ez megfelel a fenti érvelésnek, és azonnal láthattuk, hogy a térkép szeletté változtatásának egy kis refaktora valóban lehetséges, és ez a kód memóriáját hatékonyabbá teheti. Tehát megváltoztattuk a következőre:

Olyan egyszerű, hogy a térkép helyett most egy szeletet használunk. Annak a módja miatt, hogy fogadjuk a lustán betöltött adatokat, és hogy később hogyan férünk hozzá ezekhez az adatokhoz, kivéve ezt a két sort és az ezeket az adatokat tároló struktúrát, nem volt szükség más kódváltásra. Mit tett a memóriafogyasztással?

Nézzük meg a benchcmpcsak néhány tesztet

Az olvasási tesztek inicializálják az adatstruktúrát, ami létrehozza az allokációkat. Láthatjuk, hogy a futásideje ~ 30% -kal javult, az allokációk 50% -kal csökkentek, és a memóriafogyasztás> 90% -kal (!)

Mivel a most szeletelt térképet soha nem töltötték fel sok elemkel, a számok nagyjából azt mutatják, hogy mit fogunk látni a gyártásban. Ez az adat entrópiától függ, de előfordulhat, hogy mind az allokáció, mind a memóriafogyasztás javulása még nagyobb lett volna.

Ha pprofújból megnézzük, és ugyanabból a tesztből halmprofilt veszünk, azt látjuk, hogy a memóriafogyasztás valójában ~ 90% -kal csökkent.

Az elvitel az lesz, hogy kisebb adathalmazok esetén ne használjon olyan térképeket, ahol a szeletek elegendőek lennének, mivel a térképeknek nagy a fejük.

Teljes mag dump

Mint említettük, itt látjuk a szerszámozás legnagyobb korlátját. Amikor ezt a kérdést vizsgáltuk, megszállottjaink voltak, hogy nagy siker nélkül eljuthassunk a gyökérobjektumhoz. A Go idővel nagy ütemben fejlődik, de ennek az evolúciónak ára van a teljes dump vagy memória reprezentáció esetén. A teljes kupacdump formátum, ahogy változik, nem kompatibilis visszafelé. Az itt leírt legfrissebb verzió, és egy teljes kupacos dump megírásához használhatja debug.WriteHeapDump().

Bár jelenleg nem találjuk magunkat „beragadtnak”, mert nincs jó megoldás a teljes szemétlerakók feltárására. pprofválaszolt minden eddigi kérdésünkre.

Ne feledje, az internet sok olyan információra emlékszik, amely már nem releváns. Íme néhány dolog, amelyet figyelmen kívül kell hagynia, ha megpróbálja megnyitni a teljes dumpot a go1.11-től kezdve:

  • MacOS-on nem lehet teljes mag dump-ot megnyitni és hibakeresni, csak Linuxon.
  • A //github.com/randall77/hprof címen található eszközök a Go1.3-ra vonatkoznak, létezik egy villa az 1.7-es vagy újabb verzióhoz, de ez sem működik megfelelően (hiányos).
  • viewcore at //github.com/golang/debug/tree/master/cmd/viewcore nem igazán áll össze. Elég könnyű kijavítani (a belső csomagok a golang.org-ra mutatnak, és nem a github.com-ra mutatnak), de ez sem működik , MacOS-on, esetleg Linuxon sem.
  • A //github.com/randall77/corelib szintén meghiúsul MacOS rendszeren

pprof UI

Az utolsó részlet, amellyel tisztában kell lenni, amikor a pprof-ról van szó, a felhasználói felület funkciója. Sokat takaríthat meg időt, amikor a pprof-rel vett profilhoz kapcsolódó bármely kérdésben vizsgálatot indít.

go tool pprof -http=:8080 heap.out

Ekkor meg kell nyitnia a webböngészőt. Ha nem, akkor keresse meg a beállított portot. Lehetővé teszi az opciók megváltoztatását és a vizuális visszajelzés sokkal gyorsabb megszerzését, mint amennyit a parancssorból megtehet. Nagyon hasznos módszer az információk felhasználására.

Az UI valójában megismerte a lángdiagramokat, amelyek nagyon gyorsan felfedik a kód hibás területeit.

Következtetés

A Go egy izgalmas nyelv, nagyon gazdag eszközkészlettel, még sok mindent megtehetsz a pprof segítségével. Ez a bejegyzés például egyáltalán nem érinti a CPU profilozását.

Néhány más jó olvasmány:

  • //rakyll.org/archive/ - Úgy gondolom, hogy ez a teljesítmény-monitorozás egyik közreműködője, sok jó bejegyzés a blogján
  • //github.com/google/gops - a JBD (aki a rakyll.org-ot futtatja) írta, ez az eszköz saját blogbejegyzését garantálja.
  • //medium.com/@cep21/using-go-1-10-new-trace-features-to-debug-an-integration-test-1dc39e4e812d - go tool traceami a CPU profilozása körül van, ez egy nagyszerű bejegyzés erről a profilalkotásról .