Verbose Garbage Collection v Jave

1. Prehľad

V tomto návode sa pozrieme na to, ako zapnúť podrobné zhromažďovanie odpadu v aplikácii Java. Začneme predstavením toho, čo je to podrobné upratovanie a prečo môže byť užitočný.

Ďalej sa pozrieme na niekoľko rôznych príkladov a dozvieme sa o rôznych dostupných možnostiach konfigurácie. Navyše, zameriame sa tiež na to, ako interpretovať výstup našich podrobných protokolov.

Ak sa chcete dozvedieť viac informácií o Garbage Collection (GC) a rôznych dostupných implementáciách, prečítajte si náš článok o Java Garbage Collectors.

2. Stručný úvod do podrobného zberu odpadu

Pri ladení a ladení mnohých problémov je často potrebné zapnutie podrobného zaznamenávania odpadu, najmä problémy s pamäťou. V skutočnosti by niekto tvrdil, že na to, aby sme mohli prísne monitorovať stav našich aplikácií, by sme mali vždy monitorovať výkonnosť JVM’s Garbage Collection.

Ako uvidíme, protokol GC je veľmi dôležitým nástrojom na odhalenie potenciálnych vylepšení haldy a konfigurácie GC našej aplikácie. Pri každej udalosti GC poskytuje protokol GC presné údaje o jej výsledkoch a trvaní.

Analýza týchto informácií nám môže v priebehu času pomôcť lepšie pochopiť správanie našej aplikácie a vyladiť výkonnosť našej aplikácie. Navyše, môže pomôcť optimalizovať frekvenciu GC a časy zberu zadaním najlepších veľkostí hromady, ďalších možností JVM a alternatívnych algoritmov GC.

2.1. Jednoduchý program Java

Na ukážku toho, ako povoliť a interpretovať naše protokoly GC, použijeme priamy program Java:

verejná trieda Aplikácia {private static Map stringContainer = new HashMap (); public static void main (String [] args) {System.out.println ("Spustenie programu!"); String stringWithPrefix = "stringWithPrefix"; // Načítanie haldy Java do 3 M java.lang.String inštancií pre (int i = 0; i <3000000; i ++) {String newString = stringWithPrefix + i; stringContainer.put (newString, newString); } System.out.println ("Veľkosť MAP:" + stringContainer.size ()); // Explicit GC! System.gc (); // Odstráňte 2 M z 3 M pre (int i = 0; i <2000000; i ++) {String newString = stringWithPrefix + i; stringContainer.remove (newString); } System.out.println ("Veľkosť MAP:" + stringContainer.size ()); System.out.println ("Koniec programu!"); }}

Ako vidíme na vyššie uvedenom príklade, tento jednoduchý program načíta 3 milióny String inštancie do a Mapa objekt. Potom urobíme explicitné volanie na zber odpadu pomocou System.gc ().

Nakoniec odstránime 2 milióny z String inštancie z Mapa. Tiež výslovne používame System.out.println uľahčiť tlmočenie výstupu.

V ďalšej časti uvidíme, ako aktivovať protokolovanie GC.

3. Aktivácia „jednoduchého“ protokolovania GC

Začnime spustením nášho programu a povolením podrobného GC prostredníctvom našich argumentov pre spustenie JVM:

-XX: + UseSerialGC -Xms1024m -Xmx1024m -verbose: gc

Dôležitým argumentom je -verbose: gc, ktorý aktivuje protokolovanie informácií o zbere odpadu v najjednoduchšej podobe. Predvolene sa do protokolu GC zapisuje stdout a mali by produkovať riadok pre každú mladú generáciu GC a každú celú GC.

Na účely nášho príkladu sme prostredníctvom argumentu určili sériový garbage collector, najjednoduchšiu implementáciu GC -XX: + UseSerialGC.

Nastavili sme tiež minimálnu a maximálnu veľkosť haldy 1024 MB, ale existuje samozrejme viac parametrov JVM, ktoré môžeme vyladiť.

3.1. Základné porozumenie podrobného výstupu

Poďme sa teraz pozrieť na výstup nášho jednoduchého programu:

Štart programu! [GC (chyba alokácie) 279616K-> 146232K (1013632K), 0,3318607 s] [GC (chyba alokácie) 425848K-> 295442K (1013632K), 0,4266943 s) Veľkosť MAP: 3000000 [Full GC (System.gc ()) 434341K- > 368279K (1013632K), 0,5420611 s]] [GC (zlyhanie pridelenia) 647895K-> 368280K (1013632K), 0,0075449 s] Veľkosť MAP: 10 000 000 Koniec programu!

Vo vyššie uvedenom výstupe už môžeme vidieť veľa užitočných informácií o tom, čo sa deje vo vnútri JVM.

Spočiatku môže byť tento výstup dosť skľučujúci, poďme si ho však postupne predstaviť.

Po prvé, vidíme, že sa uskutočnili štyri zbierky, jedna Full GC a tri upratovacie mladé generácie.

3.2. Podrobný výstup

Poďme si podrobnejšie rozložiť výstupné riadky, aby sme presne pochopili, o čo ide:

  1. GC alebo Úplné GCTyp zberu odpadu GC alebo Úplné GC rozlíšiť menší alebo úplný zber odpadu
  2. (Zlyhanie pridelenia) alebo (System.gc ()) - príčina zbierky - Zlyhanie pridelenia naznačuje, že v Edene už nezostalo viac miesta na alokovanie našich objektov
  3. 279616K-> 146232K - Obsadená halda pamäte pred a za GC (oddelená šípkou)
  4. (1013632K) - Aktuálna kapacita haldy
  5. 0,3318607 s - Trvanie udalosti GC v sekundách

Ak teda vezmeme prvý riadok, 279616K-> 146232K (1013632K) znamená, že GC znížil obsadenú haldu pamäte z 279616K do 146232K. Kapacita haldy v čase GC bola 1013632K, a GC vzal 0.3318607 sekúnd.

Aj keď môže byť jednoduchý formát protokolovania GC užitočný, poskytuje obmedzené podrobnosti. Napríklad nemôžeme povedať, či GC presunul nejaké objekty z mladej na starú generáciu alebo aká bola celková veľkosť mladej generácie pred a po každej zbierke.

Z tohto dôvodu je podrobné protokolovanie GC užitočnejšie ako jednoduché.

4. Aktivácia „podrobného“ protokolovania GC

Na aktiváciu podrobného protokolovania GC použijeme argument -XX: + PrintGCDetails. Získate tým ďalšie podrobnosti o každej GC, napríklad:

  • Veľkosť mladej a starej generácie pred a po každej VP
  • Čas potrebný na to, aby sa GC stalo u mladej i starej generácie
  • Veľkosť objektov propagovaných na každom GC
  • Súhrn veľkosti celej haldy

V nasledujúcom príklade uvidíme, ako zachytiť ešte podrobnejšie informácie v našich protokoloch -verbose: gc s týmto argumentom navyše.

Upozorňujeme, že -XX: + PrintGCDetails vlajka bola v Jave 9 zastaraná v prospech nového zjednoteného protokolovacieho mechanizmu (viac o tom neskôr). Každopádne nový ekvivalent -XX: + PrintGCDetails je -Xlog: gc * možnosť.

5. Interpretácia „podrobného“ podrobného výstupu

Znova spustime náš ukážkový program:

-XX: + UseSerialGC -Xms1024m -Xmx1024m -verbose: gc -XX: + PrintGCDetails

Tentokrát je výstup skôr verbálny:

Štart programu! [GC (Allocation Failure) [DefNew: 279616K-> 34944K (314560K), 0,3626923 s] 279616K-> 146232K (1013632K), 0,3627492 s]] [Časy: používateľ = 0,33 sys = 0,03, skutočné = 0,36 s]] [GC (pridelenie) Zlyhanie) [DefNew: 314560K-> 34943K (314560K), 0,4589079 s] 425848K-> 295442K (1013632K), 0,4589526 s]] [Časy: používateľ = 0,41 sys = 0,05, reálne = 0,46 s] Veľkosť MAP: 3000000 [Full GC ( System.gc ()) [Tenured: 260498K-> 368281K (699072K), 0,5580183 s] 434341K-> 368281K (1013632K), [Metaspace: 2624K-> 2624K (1056768K)], 0,5580738 s] [Časy: používateľ = 0,50 sys = 0,06, reálne = 0,56 s]] [GC (zlyhanie alokácie) [DefNew: 279616K-> 0K (314560K), 0,0076722 s] 647897K-> 368281K (1013632K), 0,0077169 s]] [Časy: používateľ = 0,01 sys = 0,00, skutočné = 0,01 s] Veľkosť MAP: 10 000 000 Koniec programu! Haldy def novej generácie celkom 314560K, používa 100261K [0x00000000c0000000, 0x00000000d5550000, 0x00000000d5550000) eden priestor 279616K, 35% používa [0x00000000c0000000, 0x00000000c61e9370, 0x00000000d1110000) z priestoru 34944K, 0% používa [0x00000000d3330000, 0x00000000d3330188, 0x00000000d5550000) do priestoru 34944K, 0% použitý [0x00000000d1110000, 0x00000000d1110000, 0x00000000d3330000) tenured generácie celkom 699072K, používa 368281K [0x00000000d5550000, 0x0000000100000000, 0x0000000100000000) priestor 699072K, používa 52% [0x00000000d5550000, 0x00000000ebcf65e0, 0x00000000ebcf6600, 0x0000000100000000) Metaspace používa 2637K, kapacita 4486K, spáchal 4864K, vyhradené 1056768K priestor triedy využitý 283K, kapacita 386K, nasadený 512K, rezervovaný 1048576K

Mali by sme byť schopní rozpoznať všetky prvky z jednoduchého protokolu GC. ale pribudlo niekoľko nových položiek.

Uvažujme teraz o nových položkách vo výstupe, ktoré sú v nasledujúcej časti zvýraznené modrou farbou:

5.1. Tlmočenie maloletej GC v mladej generácii

Začneme analýzou nových častí v malom GC:

  • [GC (zlyhanie pridelenia) [DefNew: 279616K-> 34944K (314560K), 0,3626923 s] 279616K-> 146232K (1013632K), 0,3627492 s] [Časy: používateľ = 0,33 sys = 0,03, skutočné = 0,36 s]

Rovnako ako predtým rozdelíme riadky na časti:

  1. DefNew - Meno použitého zberača odpadu. Tento nie tak zrejmý názov znamená jednozávitový zberač odpadu a stopiek na svete a slúži na čistenie mladej generácie.
  2. 279616K-> 34944K - Využitie mladej generácie pred a po zbere
  3. (314560 tis.) - Celková veľkosť mladej generácie
  4. 0,3626923 s - trvanie v sekundách
  5. [Časy: používateľ = 0,33 sys = 0,03, reálne = 0,36 s] - Trvanie udalosti GC, merané v rôznych kategóriách

Teraz si vysvetlíme rôzne kategórie:

  • používateľ - Celkový čas CPU, ktorý spotreboval program Garbage Collector
  • sys - Čas strávený hovormi OS alebo čakaním na udalosti systému
  • reálny - Toto je všetok uplynulý čas vrátane časových úsekov použitých inými procesmi

Pretože používame náš príklad pomocou programu Serial Garbage Collector, ktorý vždy používa iba jedno vlákno, v reálnom čase sa rovná súčtu časov používateľov a systému.

5.2. Tlmočenie úplnej GC

V tomto predposlednom príklade vidíme, že pre veľkú zbierku (Full GC), ktorú spustilo naše systémové volanie, bol použitý zberač V držbe.

Poslednou časťou ďalších informácií, ktorú vidíme, je rozdelenie podľa rovnakého vzoru pre Metaspace:

[Metaspace: 2624K -> 2624K (1056768K)], 0,5580738 s]

Metaspace je nový pamäťový priestor zavedený v prostredí Java 8 a je oblasťou natívnej pamäte.

5.3. Analýza rozpadu haldy Java

Záverečná časť výstupu obsahuje rozdelenie haldy vrátane súhrnu pamäťovej stopy pre každú časť pamäte.

Vidíme, že priestor Eden mal stopu 35% a Tenured mal stopu 52%. Zahrnuté je aj zhrnutie pre priestor metadát a priestor triedy.

Z vyššie uvedených príkladov teraz môžeme presne pochopiť, čo sa dialo so spotrebou pamäte vo vnútri JVM počas udalostí GC.

6. Pridanie informácií o dátume a čase

Žiadny dobrý denník nie je úplný bez informácií o dátume a čase.

Tieto ďalšie informácie môžu byť veľmi užitočné, keď potrebujeme korelovať údaje protokolu GC s údajmi z iných zdrojov, alebo môžu jednoducho pomôcť uľahčiť vyhľadávanie.

Nasledujúce dva argumenty môžeme pridať, keď spustíme našu aplikáciu, aby sa v našich denníkoch zobrazili informácie o dátume a čase:

-XX: + PrintGCTimeStamps -XX: + PrintGCDateStamps

Každý riadok teraz začína absolútnym dátumom a časom, kedy bol napísaný, za ktorým nasleduje časová pečiatka odrážajúca skutočný čas prechádzajúci v sekundách od začiatku JVM:

2018-12-11T02: 55: 23.518 + 0100: 2.601: [GC (pridelenie ...

Upozorňujeme, že tieto ladiace príznaky boli odstránené v prostredí Java 9. Nová alternatíva je:

-Xlog: gc * :: čas

7. Prihlásenie do súboru

Ako sme už videli, štandardne sa do protokolu GC zapisuje stdout. Praktickejším riešením je zadanie výstupného súboru.

Môžeme to urobiť pomocou argumentu -Xloggc: kde spis je absolútna cesta k nášmu výstupnému súboru:

-Xloggc: /path/to/file/gc.log

Podobne ako iné ladiace príznaky, aj Java 9 zastarala príznak -Xloggc v prospech nového zjednoteného protokolovania. Aby som bol konkrétnejší, alternatívou prihlásenia do súboru je teraz:

-Xlog: gc: /path/to/file/gc.log

8. Java 9: ​​Unified JVM Logging

Od verzie Java 9 je väčšina ladiacich príznakov súvisiacich s GC zastaraná v prospech možnosti zjednoteného protokolovania -Xlog: gc. The podrobné: gc táto možnosť však stále funguje v prostredí Java 9 a novšej verzii.

Napríklad ako Java 9, ekvivalent súboru -verbose: gc príznak v novom systéme zjednoteného protokolovania je:

-Xlog: gc

Týmto sa zapíšu všetky protokoly GC na úrovni informácií na štandardný výstup. Je tiež možné použiť -Xlog: gc = syntax na zmenu úrovne protokolu. Napríklad na zobrazenie všetkých protokolov na úrovni ladenia:

-Xlog: gc = ladenie

Ako sme videli skôr, cieľ výstupu môžeme zmeniť pomocou -Xlog: gc =: syntax. V predvolenom nastavení je výkon je stdout, ale môžeme to zmeniť na stderr alebo dokonca súbor:

-Xlog: gc = debug: súbor = gc.txt

Je tiež možné pridať niekoľko ďalších polí k výstupu pomocou dekoratérov. Napríklad:

-Xlog: gc = debug :: pid, čas, doba prevádzkyschopnosti

Tu tlačíme ID procesu, dobu prevádzky a aktuálnu časovú značku v každom výpise protokolu.

Ďalšie príklady zjednoteného protokolovania JVM nájdete v štandarde JEP 158.

9. A Nástroj na analýzu protokolov GC

Analyzovať protokoly GC pomocou textového editora môže byť časovo náročné a dosť zdĺhavé. V závislosti od verzie JVM a použitého algoritmu GC sa formát protokolu GC môže líšiť.

Existuje veľmi dobrý bezplatný nástroj na grafickú analýzu, ktorý analyzuje protokoly zhromažďovania odpadu, poskytuje mnoho metrík o potenciálnych problémoch s zhromažďovaním odpadu a dokonca poskytuje potenciálne riešenia týchto problémov.

Určite si pozrite Universal GC Log Analyzer!

10. Záver

Ak to zhrnieme, v tomto tutoriáli sme podrobne preskúmali podrobný zber odpadu v Jave.

Najprv sme začali predstavením toho, čo je to verbose odvoz odpadu a prečo by sme ho mohli chcieť použiť. Potom sme sa pozreli na niekoľko príkladov pomocou jednoduchej aplikácie Java. Začali sme povolením protokolovania GC v najjednoduchšej podobe, skôr ako sme preskúmali niekoľko podrobnejších príkladov a spôsobu interpretácie výstupu.

Nakoniec sme preskúmali niekoľko ďalších možností na zaznamenávanie informácií o čase a dátume a spôsobe zápisu informácií do súboru denníka.

Príklady kódov nájdete na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found