Megosztás:


Memóriaszivárgás hibakeresése a .NET-ben

Ez a cikk a következőre vonatkozik: ✔️ .NET Core 3.1 SDK és újabb verziók

A memória kiszivároghat, ha az alkalmazás olyan objektumokra hivatkozik, amelyekre már nincs szüksége a kívánt feladat végrehajtásához. Az objektumokra való hivatkozás megakadályozza, hogy a szemétgyűjtő visszanyerje a felhasznált memóriát. Ez teljesítménycsökkenést okozhat, és OutOfMemoryException kivétel dobását eredményezheti.

Ez az oktatóanyag bemutatja azokat az eszközöket, amelyek segítségével elemezheti a memóriaszivárgást egy .NET-alkalmazásban a .NET diagnosztikai parancssori eszközeivel. Ha Windows rendszeren dolgozik, előfordulhat, hogy a Visual Studio memóriadiagnosztikai eszközeit használhatja a memóriaszivárgás hibakereséséhez.

Ez az oktatóanyag egy mintaalkalmazást használ, amely szándékosan kiszivárogtatja a memóriát gyakorlatként. Elemezheti azokat az alkalmazásokat is, amelyek véletlenül kiszivárogtatják a memóriát.

Ebben az oktatóanyagban a következőket meg fogja tanulni:

  • A felügyelt memóriahasználat vizsgálata dotnet-counters használatával.
  • Memóriaképfájl létrehozása.
  • Elemezze a memóriahasználatot a memóriaképfájl használatával.

Előfeltételek

Az oktatóanyag a következőket használja:

Az oktatóanyag feltételezi, hogy a mintaalkalmazások és -eszközök telepítve vannak, és használatra készek.

Ha az alkalmazás a .NET 9-nél régebbi .NET-verziót futtat, a dotnet-számlálók kimeneti felhasználói felülete kissé eltérő lesz; részletekért lásd: dotnet-counters .

Felügyelt memóriahasználat vizsgálata

Mielőtt elkezdené összegyűjteni a diagnosztikai adatokat, hogy segítsen a forgatókönyv kiváltó okának elhárításában, győződjön meg arról, hogy valóban memóriaszivárgást tapasztal (a memóriahasználat növekedése). Ezt a dotnet-counters eszközzel ellenőrizheti.

Nyisson meg egy konzolablakot, és keresse meg azt a könyvtárat, ahol letöltötte és kibontotta a hibakeresési célpéldányt. A cél futtatása:

dotnet run

Egy külön konzolon keresse meg a folyamatazonosítót:

dotnet-counters ps

A kimenetnek a következőhöz hasonlónak kell lennie:

4807 DiagnosticScena /home/user/git/samples/core/diagnostics/DiagnosticScenarios/bin/Debug/netcoreapp3.0/DiagnosticScenarios

Megjegyzés:

Ha az előző parancs nem működik, vagy nem található, valószínűleg először telepítenie kell az dotnet-counters eszközt. Használja a következő parancsot:

dotnet tool install --global dotnet-counters

Most ellenőrizze a felügyelt memóriahasználatot a dotnet-counters eszközzel. A --refresh-interval frissítés közötti másodpercek számát adja meg:

dotnet-counters monitor --refresh-interval 1 -p 4807

Az élő kimenetnek a következőhöz hasonlónak kell lennie:

Press p to pause, r to resume, q to quit.
Status: Running

Name                                                            Current Value
[System.Runtime]
    dotnet.assembly.count ({assembly})                              111
    dotnet.gc.collections ({collection})
    gc.heap.generation
    ------------------
    gen0                                                              1
    gen1                                                              0
    gen2                                                              0
    dotnet.gc.heap.total_allocated (By)                       4,431,712
    dotnet.gc.last_collection.heap.fragmentation.size (By)
    gc.heap.generation
    ------------------
    gen0                                                        803,576
    gen1                                                         15,456
    gen2                                                              0
    loh                                                               0
    poh                                                               0
    dotnet.gc.last_collection.heap.size (By)
    gc.heap.generation
    ------------------
    gen0                                                        811,960
    gen1                                                      1,214,720
    gen2                                                              0
    loh                                                               0
    poh                                                          24,528
    dotnet.gc.last_collection.memory.committed_size (By)      4,296,704
    dotnet.gc.pause.time (s)                                          0.003
    dotnet.jit.compilation.time (s)                                   0.329
    dotnet.jit.compiled_il.size (By)                            120,212
    dotnet.jit.compiled_methods ({method})                            1,202
    dotnet.monitor.lock_contentions ({contention})                    2
    dotnet.process.cpu.count ({cpu})                                 22
    dotnet.process.cpu.time (s)
    cpu.mode
    --------
    system                                                            0.344
    user                                                              0.344
    dotnet.process.memory.working_set (By)                   64,331,776
    dotnet.thread_pool.queue.length ({work_item})                     0
    dotnet.thread_pool.thread.count ({thread})                        0
    dotnet.thread_pool.work_item.count ({work_item})                  7
    dotnet.timer.count ({timer})                                      0

Az alábbi sorra összpontosítva:

    dotnet.gc.last_collection.memory.committed_size (By)   4,296,704

Láthatja, hogy a felügyelt halommemória közvetlenül az indítás után 4 MB.

Most nyissa meg az URL-címet https://localhost:5001/api/diagscenario/memleak/20000.

Figyelje meg, hogy a memóriahasználat több mint 20 MB-ra nőtt.

    dotnet.gc.last_collection.memory.committed_size (By)   21,020,672

A memóriahasználat figyelésével nyugodtan kijelentheti, hogy a memória növekszik vagy kiszivárog. A következő lépés a megfelelő adatok összegyűjtése a memóriaelemzéshez.

Memóriakép létrehozása

A lehetséges memóriavesztések elemzésekor hozzá kell férnie az alkalmazás memóriakészletéhez a memória tartalmának elemzéséhez. Az objektumok közötti kapcsolatokat vizsgálva elméleteket hozhat létre arról, hogy miért nem szabadít fel memóriát. Gyakori diagnosztikai adatforrás a Windows memóriaképe, vagy a Linuxon ezzel egyenértékű mag memóriaképe. A .NET-alkalmazások memóriaképének létrehozásához használhatja a dotnet-dump eszközt.

A korábban elindított hibakeresési mintacél használatával futtassa a következő parancsot egy Linux-mag memóriakép létrehozásához:

dotnet-dump collect -p 4807

Az eredmény egy központi memóriakép, amely ugyanabban a mappában található.

Writing minidump with heap to ./core_20190430_185145
Complete

Megjegyzés:

Az idő múlásával történő összehasonlításhoz folytassa az eredeti folyamat futtatását az első memóriakép összegyűjtése után, majd ugyanúgy gyűjtsön egy második memóriaképet. Ezután két memóriadumpot fog létrehozni egy bizonyos időszakon keresztül, amelyeket összehasonlíthat, hogy lássa, hol növekszik a memóriahasználat.

A sikertelen folyamat újraindítása

A memóriakép összegyűjtése után elegendő információval kell rendelkeznie a meghiúsult folyamat diagnosztizálásához. Ha a sikertelen folyamat egy éles kiszolgálón fut, a folyamat újraindításával ideális idő a rövid távú szervizelésre.

Ezzel az oktatóanyaggal befejezte a minta hibakeresési célt, és bezárhatja azt. Lépjen a kiszolgálót indító terminálra, és nyomja le a Ctrl+C billentyűkombinációt.

A magmemória elemzése

Most, hogy létrehozott egy alapmemóriaképet, használja a dotnet-dump eszközt a memóriakép elemzéséhez:

dotnet-dump analyze core_20190430_185145

Hol core_20190430_185145 található az elemezni kívánt alapvető memóriakép neve.

Megjegyzés:

Ha hibaüzenet jelenik meg, amely arra panaszkodik, hogy libdl.so nem található, előfordulhat, hogy telepítenie kell a libc6-dev csomagot. További információ: A .NET előfeltételei Linuxon.

Ekkor megjelenik egy üzenet, amelyben SOS-parancsokat adhat meg. Általában az első dolog, amit meg szeretne nézni, a kezelt kupac teljes állapota:

> dumpheap -stat

Statistics:
              MT    Count    TotalSize Class Name
...
00007f6c1eeefba8      576        59904 System.Reflection.RuntimeMethodInfo
00007f6c1dc021c8     1749        95696 System.SByte[]
00000000008c9db0     3847       116080      Free
00007f6c1e784a18      175       128640 System.Char[]
00007f6c1dbf5510      217       133504 System.Object[]
00007f6c1dc014c0      467       416464 System.Byte[]
00007f6c21625038        6      4063376 testwebapi.Controllers.Customer[]
00007f6c20a67498   200000      4800000 testwebapi.Controllers.Customer
00007f6c1dc00f90   206770     19494060 System.String
Total 428516 objects

Itt láthatja, hogy a legtöbb objektum vagy String vagy Customer objektum.

A metódustáblával (MT) ismét használhatja a dumpheap parancsot az összes példány listájának String lekéréséhez:

> dumpheap -mt 00007f6c1dc00f90

         Address               MT     Size
...
00007f6ad09421f8 00007faddaa50f90       94
...
00007f6ad0965b20 00007f6c1dc00f90       80
00007f6ad0965c10 00007f6c1dc00f90       80
00007f6ad0965d00 00007f6c1dc00f90       80
00007f6ad0965df0 00007f6c1dc00f90       80
00007f6ad0965ee0 00007f6c1dc00f90       80

Statistics:
              MT    Count    TotalSize Class Name
00007f6c1dc00f90   206770     19494060 System.String
Total 206770 objects

Mostantól a gcroot parancsot használhatja a System.String példányon, hogy megtekintse, hogyan és miért van gyökerezve az objektum.

> gcroot 00007f6ad09421f8

Thread 3f68:
    00007F6795BB58A0 00007F6C1D7D0745 System.Diagnostics.Tracing.CounterGroup.PollForValues() [/_/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterGroup.cs @ 260]
        rbx:  (interior)
            ->  00007F6BDFFFF038 System.Object[]
            ->  00007F69D0033570 testwebapi.Controllers.Processor
            ->  00007F69D0033588 testwebapi.Controllers.CustomerCache
            ->  00007F69D00335A0 System.Collections.Generic.List`1[[testwebapi.Controllers.Customer, DiagnosticScenarios]]
            ->  00007F6C000148A0 testwebapi.Controllers.Customer[]
            ->  00007F6AD0942258 testwebapi.Controllers.Customer
            ->  00007F6AD09421F8 System.String

HandleTable:
    00007F6C98BB15F8 (pinned handle)
    -> 00007F6BDFFFF038 System.Object[]
    -> 00007F69D0033570 testwebapi.Controllers.Processor
    -> 00007F69D0033588 testwebapi.Controllers.CustomerCache
    -> 00007F69D00335A0 System.Collections.Generic.List`1[[testwebapi.Controllers.Customer, DiagnosticScenarios]]
    -> 00007F6C000148A0 testwebapi.Controllers.Customer[]
    -> 00007F6AD0942258 testwebapi.Controllers.Customer
    -> 00007F6AD09421F8 System.String

Found 2 roots.

Láthatja, hogy az String objektum közvetlenül az Customer objektum tulajdonában van, és közvetetten egy CustomerCache objektum tartja.

Folytathatja az objektumok memóriakiírását, így láthatja, hogy a legtöbb String objektum hasonló mintát követ. Ezen a ponton a vizsgálat elegendő információt szolgáltatott a kód alapvető okának azonosításához.

Ez az általános eljárás lehetővé teszi a főbb memóriaszivárgások forrásának azonosítását.

Erőforrások tisztítása

Ebben az oktatóanyagban elindított egy minta webkiszolgálót. Ezt a kiszolgálót le kellett volna állítani a sikertelen folyamat újraindítása szakaszban leírtak szerint.

A létrehozott memóriaképfájlt is törölheti.

Lásd még

Következő lépések