Programiranje

Diagnosticirajte pogoste težave med izvajanjem s hprof

Puščanje pomnilnika in blokade ter CPU-ji, oh moj! Razvijalci aplikacij Java se pogosto soočajo s temi težavami pri izvajanju. Lahko so še posebej zastrašujoči v zapleteni aplikaciji z več nitmi, ki potekajo skozi stotisoče vrstic kode - aplikacije, ki je ne morete poslati, ker raste v pomnilniku, postane neaktivna ali požre več procesorskih ciklov, kot bi morala.

Nobena skrivnost ni, da so bila orodja za profiliranje Java dolga leta, da so ujela svoje kolege iz drugih jezikov. Zdaj obstaja veliko močnih orodij, ki nam pomagajo izslediti krivce za te pogoste težave. Kako pa razvijete zaupanje v svojo sposobnost učinkovite uporabe teh orodij? Konec koncev uporabljate orodja za diagnosticiranje zapletenega vedenja, ki ga ne razumete. Podatki, ki jih zagotavljajo orodja, so zapleteni in informacije, ki jih iščete ali niso, niso vedno jasne.

Ko sem se v svoji prejšnji inkarnaciji kot eksperimentalni fizik soočal s podobnimi težavami, sem ustvaril kontrolne poskuse s predvidljivimi rezultati. To mi je pomagalo pridobiti zaupanje v merilni sistem, ki sem ga uporabljal v poskusih, ki so dajali manj predvidljive rezultate. Podobno ta članek uporablja orodje za profiliranje hprof, da preuči tri preproste kontrolne aplikacije, ki kažejo tri zgoraj navedena pogosta vedenja. Čeprav ni tako uporabniku prijazen kot nekatera komercialna orodja na trgu, je hprof vključen v Java 2 JDK in, kot bom pokazal, lahko učinkovito diagnosticira to vedenje.

Zaženite s hprof

Zagon programa s hprof je enostaven. Preprosto prikličite izvajalno okolje Jave z naslednjo možnostjo ukazne vrstice, kot je opisano v dokumentaciji orodja JDK za zaganjalnik aplikacij Java:

java -Xrunhprof [: pomoč] [: =, ...] MyMainClass 

Seznam podmožnosti je na voljo z [: pomoč] prikazana možnost. Primere v tem članku sem ustvaril z uporabo vrat Blackdown JDK 1.3-RC1 za Linux z naslednjim ukazom za zagon:

java -classic -Xrunhprof: kopica = mesta, CPU = vzorci, globina = 10, monitor = y, nit = y, doe = y MemoryLeak 

Naslednji seznam pojasnjuje delovanje vsake podmožnosti, uporabljene v prejšnjem ukazu:

  • kopica = spletna mesta: Hprof pove, da ustvari sledi sklada, ki kažejo, kje je bil dodeljen pomnilnik
  • CPU = vzorci: Hprofu pove, naj s statističnim vzorčenjem določi, kje CPU preživi svoj čas
  • globina = 10: Hprof pove, naj prikaže sledi sklada največ 10 stopenj globoko
  • monitor = y: Pove hprof, da ustvari informacije o nadzornikih sporov, ki se uporabljajo za sinhronizacijo dela več niti
  • nit = y: Pove hprof, da identificira niti v sledovih skladov
  • srna = y: Hprofu pove, naj ob izhodu ustvari izpis podatkov profiliranja

Če uporabljate JDK 1.3, morate izklopiti privzeti prevajalnik HotSpot z -klasična možnost. HotSpot ima svoj profilirnik, ki se prikliče prek -Xprof možnost, ki uporablja izhodni format, drugačen od tistega, ki ga bom opisal tukaj.

Zagon programa s hprof bo pustil datoteko z imenom java.hprof.txt v vašem delovnem imeniku; ta datoteka vsebuje podatke o profiliranju, zbrane med izvajanjem programa. Odlagališče lahko ustvarite tudi kadar koli, ko se program izvaja, tako da pritisnete Ctrl- \ v oknu konzole Java na Unixu ali Ctrl-Break v sistemu Windows.

Anatomija izhodne datoteke hprof

Izhodna datoteka hprof vključuje odseke, ki opisujejo različne značilnosti profiliranega programa Java. Začne se z glavo, ki opisuje njeno obliko, za katero glava trdi, da se lahko spremeni brez predhodnega obvestila.

Odseki Thread in Trace izhodne datoteke vam pomagajo ugotoviti, katere niti so bile aktivne, ko se je program zagnal in kaj so storile. Odsek Thread vsebuje seznam vseh niti, ki so se začele in zaključile v življenju programa. Odsek Trace vključuje seznam oštevilčenih sledov skladov za nekatere niti. Številke sledov skladov so v drugih odsekih datotek navzkrižno sklicevane.

Odseki Heap Dump in Sites vam pomagajo analizirati porabo pomnilnika. Odvisno od kup podmožnost, ki jo izberete ob zagonu navideznega stroja (VM), lahko na kopici Jave dobite izpis vseh predmetov v živo (kopica = dump) in / ali razvrščen seznam mest dodeljevanja, ki opredeljuje najbolj dodeljene predmete (kopica = spletna mesta).

Vzorci CPU in CPU Time vam pomagajo razumeti uporabo CPU; razdelek, ki ga dobite, je odvisen od vašega procesor podmožnost (CPU = vzorci ali CPU = čas). CPU Samples ponuja statistični profil izvedbe. CPU Time vključuje meritve, kolikokrat je bila klicana določena metoda in kako dolgo je trajala izvedba posamezne metode.

Razdelka Monitor Time in Monitor Dump vam pomagata razumeti, kako sinhronizacija vpliva na delovanje vašega programa. Monitor Time prikazuje, koliko časa se vaše niti spopadajo z zaklenjenimi viri. Monitor Dump je posnetek monitorjev, ki se trenutno uporabljajo. Kot boste videli, je Monitor Dump uporaben za iskanje zastojev.

Diagnosticirajte puščanje pomnilnika

V Javi puščanje pomnilnika opredeljujem kot (običajno) nenamerno neuspešno preusmeritev zavrženih predmetov, tako da zbiralnik smeti ne more povrniti pomnilnika, ki ga uporabljajo. The MemoryLaak program v seznamu 1 je preprost:

Seznam 1. Program MemoryLeak

01 uvoz java.util.Vector; 02 03 javni razred MemoryLeak {04 05 public static void main (String [] args) {06 07 int MAX_CONSUMERS = 10000; 08 int SLEEP_BETWEEN_ALLOCS = 5; 09 10 ConsumerContainer objectHolder = nov ConsumerContainer (); 11 12 while (objectHolder.size () <MAX_CONSUMERS) {13 System.out.println ("Dodelitev predmeta" + 14 Integer.toString (objectHolder.size ()) 15); 16 objectHolder.add (new MemoryConsumer ()); 17 poskusite {18 Thread.currentThread (). Sleep (SLEEP_BETWEEN_ALLOCS); 19} catch (InterruptedException ie) {20 // Ne delaj ničesar. 21} 22} // medtem. 23} // glavno. 24 25} // Konec uhajanja spomina. 26 27 / ** Poimenovan razred vsebnika za shranjevanje referenc na objekt. Razred ConsumerContainer razreda * / 28 razširi vektor {} 29 30 / ** Razred, ki porabi fiksno količino pomnilnika. * / 31 class MemoryConsumer {32 javni statični končni int MEMORY_BLOCK = 1024; 33 javni bajt [] memoryHoldingArray; 34 35 MemoryConsumer () {36 memoryHoldingArray = nov bajt [MEMORY_BLOCK]; 37} 38} // End MemoryConsumer. 

Ko se program zažene, ustvari datoteko ConsumerContainer predmeta, nato začne ustvarjati in dodajati MemoryConsumer predmeti, veliki najmanj 1 KB ConsumerContainer predmet. Če predmeti ostanejo dostopni, niso na voljo za zbiranje smeti, kar simulira uhajanje spomina.

Oglejmo si izbrane dele datoteke profila. Prvih nekaj vrstic odseka Sites jasno prikazuje, kaj se dogaja:

MESTA ZAČETEK (urejeno po bajtih v živo) pon. September 3 19:16:29 2001 odstotek dodeljenih razredov skladov rang samoakumulacija bajtov objs bajtov objs ime sledi 1 97,31% 97,31% 10280000 10000 10280000 10000 1995 [B 2 0,39% 97,69% 40964 1 81880 10 1996 [L; 3 0,38% 98,07% 40000 10000 40000 10000 1994 Potrošnik pomnilnika 4 0,16% 98,23% 16388 1 16388 1 1295 [C 5 0,16% 98,38% 16388 1 16388 1 1304 [C ... 

Obstaja 10.000 predmetov tipa bajt [] ([B v VM-govoriti), pa tudi 10.000 MemoryConsumer predmetov. Niz bajtov zavzame 10.280.000 bajtov, zato očitno obstajajo režijski stroški tik nad surovimi bajti, ki jih porabi vsako polje. Ker je število dodeljenih predmetov enako številu predmetov v živo, lahko sklepamo, da nobenega od teh predmetov ni mogoče zbirati. To je v skladu z našimi pričakovanji.

Še ena zanimiva točka: spomin, ki ga je porabil MemoryConsumer predmeti ne vključuje pomnilnika, ki ga zasedajo bajtne matrike. To kaže, da naše orodje za profiliranje ne izpostavlja hierarhičnih odnosov zadrževanja, temveč statistiko po razredih. To je pomembno razumeti, ko uporabljate hprof za ugotavljanje uhajanja pomnilnika.

Od kod pa ti puščajoči bajtni nizi? Upoštevajte, da MemoryConsumer predmetov in bajtnih nizov referenčne sledi 1994 in 1995 v naslednjem razdelku Trace. Glej, glej, te sledi nam povedo, da MemoryConsumer predmeti so bili ustvarjeni v MemoryLaak razredov glavni () in da so bajtna polja ustvarjena v konstruktorju (() metoda v VM-govoru). Ugotovili smo, da nam pušča spomin, številke vrstic in vse:

TRACE 1994: (nit = 1) MemoryLeak.main (MemoryLeak.java:16) TRACE 1995: (nit = 1) MemoryConsumer. (MemoryLeak.java:36) MemoryLeak.main (MemoryLeak.java:16) 

Diagnosticirajte CPU prašiča

V seznamu 2 je a Zasedeno delo class ima vsak klic niti metodo, ki regulira, koliko nit deluje, tako da spreminja čas spanja med obdobji izvajanja CPU-intenzivnih izračunov:

Seznam 2. Program CPUHog

01 / ** Glavni razred za kontrolni test. * / 02 javni razred CPUHog {03 javna statična void main (String [] args) {04 05 Navojni navoj, workingStiff, deloholik; 06 slouch = nov Slouch (); 07 workingStiff = novo WorkingStiff (); 08 deloholik = nov deloholik (); 09 10 slouch.start (); 11 workingStiff.start (); 12 workaholic.start (); 13} 14} 15 16 / ** Nizka poraba procesorja. * / 17 razred Slouch razširja nit {18 public Slouch () {19 super ("Slouch"); 20} 21 javni void run () {22 BusyWork.slouch (); 23} 24} 25 26 / ** Srednja nit za uporabo CPU-ja. * / 27 class WorkingStiff razširi nit {28 public WorkingStiff () {29 super ("WorkingStiff"); 30} 31 javni void run () {32 BusyWork.workNormally (); 33} 34} 35 36 / ** Visoka nit izkoriščenosti procesorja. * / 37 razred Deloholik podaljša nit {38 javni deloholik () {39 super ("deloholik"); 40} 41 javni void run () {42 BusyWork.workTillYouDrop (); 43} 44} 45 46 / ** Razred s statičnimi metodami za porabo različnih količin 47 * procesorskega časa. * / 48 razred BusyWork {49 50 javni statični int callCount = 0; 51 52 javna statična void slouch () {53 int SLEEP_INTERVAL = 1000; 54 computeAndSleepLoop (SLEEP_INTERVAL); 55} 56 57 public static void workNormally () {58 int SLEEP_INTERVAL = 100; 59 computeAndSleepLoop (SLEEP_INTERVAL); 60} 61 62 javna statična praznina workTillYouDrop () {63 int SLEEP_INTERVAL = 10; 64 computeAndSleepLoop (SLEEP_INTERVAL); 65} 66 67 zasebna statična praznina computeAndSleepLoop (int sleepInterval) {68 int MAX_CALLS = 10000; 69 while (callCount <MAX_CALLS) {70 computeAndSleep (sleepInterval); 71} 72} 73 74 zasebna statična praznina computeAndSleep (int sleepInterval) {75 int RAČUNALNIŠTVA = 1000; 76 dvojni rezultat; 77 78 // Izračunaj. 79 callCount ++; 80 za (int i = 0; i <RAČUNALNIŠTVA; i ++) {81 rezultat = Math.atan (callCount * Math.random ()); 82} 83 84 // Spanje. 85 poskusite {86 Thread.currentThread (). Sleep (sleepInterval); 87} catch (InterruptedException ie) {88 // Ne delaj ničesar. 89} 90 91} // Konec computeAndSleep. 92} // Konec zasedenega dela. 

Obstajajo tri teme - Deloholik, WorkingStiff, in Slouch - katerih delovna etika se razlikuje glede na velikost, sodeč po delu, ki se ga odločijo. Preglejte spodnji razdelek vzorcev CPU profila. Tri najvišje uvrščene sledi kažejo, da je CPU večino svojega časa porabil za izračun naključnih števil in tangent loka, kot bi pričakovali:

VZORCI CPU ZAČNIJO (skupaj = 935) Torek, 4. september 20:44:49 2001 uvrstitev metoda samoakumulacije sledenja 1 39,04% 39,04% 365 2040 java / util / Random.next 2 26,84% 65,88% 251 2042 java / util / Random. nextDouble 3 10,91% 76,79% 102 2041 java / lang / StrictMath.atan 4 8,13% 84,92% 76 2046 BusyWork.computeAndSleep 5 4,28% 89,20% 40 2050 java / lang / Math.atan 6 3,21% 92,41% 30 2045 java / lang / Math.random 7 2,25% 94,65% 21 2051 java / lang / Math.random 8 1,82% 96,47% 17 2044 java / util / Random.next 9 1,50% 97,97% 14 2043 java / util / Random.nextDvojnik 10 0,43% 98,40% 4 2047 BusyWork.computeAndSleep 11 0,21% 98,61% 2 2048 java / lang / StrictMath.atan 12 0,11% 98,72% 1 1578 java / io / BufferedReader.readLine 13 0,11% 98,82% 1 2054 java / lang / Thread.sleep 14 0,11% 98,93% 1 1956 java / security / PermissionCollection.setReadOnly 15 0,11% 99,04% 1 2055 java / lang / Thread.sleep 16 0,11% 99,14% 1 1593 java / lang / String.valueOf 17 0,11% 99,25% 1 2052 java / lang / Math.random 18 0,11% 99,36% 1 2049 java / util / Random.nextDvojnik 19 0,11% 99,47% 1 2031 BusyWork.computeAndSleep 20 0,11% 99,57% 1 1530 sun / io / CharToByteISO8859_1.convert ... 

Upoštevajte, da klici na BusyWork.computeAndSleep () metode za 8,13 odstotka, 0,43 odstotka in 0,11 odstotka za Deloholik, WorkingStiff, in Slouch niti. Katere niti so to, lahko ugotovimo tako, da preučimo sledi, omenjene v stolpcu sledi odseka CPU Samples (uvrstitve 4, 10 in 19) v naslednjem odseku Trace:

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