Programavimas

Su „Hprof“ diagnozuokite įprastas vykdymo laiko problemas

Atminties nutekėjimas ir aklavietės bei procesoriaus šernai, oi! „Java“ programų kūrėjai dažnai susiduria su šiomis vykdymo laiko problemomis. Jie gali būti ypač bauginantys sudėtingoje programoje, kurioje yra kelios gijos, einančios per šimtus tūkstančių kodo eilučių - programos, kurios negalite išsiųsti, nes ji auga atmintyje, tampa neaktyvi arba sukanda daugiau procesoriaus ciklų, nei turėtų.

Niekam ne paslaptis, kad „Java“ profiliavimo įrankiai turėjo ilgą kelią, kad pasiektų savo kolegas alternatyviomis kalbomis. Dabar egzistuoja daugybė galingų įrankių, padedančių mums rasti tų bendrų problemų kaltininkus. Bet kaip išsiugdyti pasitikėjimą savo galimybėmis efektyviai naudoti šias priemones? Galų gale jūs naudojate įrankius, kad diagnozuotumėte sudėtingą elgesį, kurio nesuprantate. Padidinant jūsų sunkumus, įrankių pateikti duomenys yra pakankamai sudėtingi, o jūsų ieškoma ar ieškoma informacija ne visada aiški.

Susidūręs su panašiomis problemomis mano ankstesniame eksperimentinio fiziko įsikūnijime, sukūriau kontrolinius eksperimentus su nuspėjamais rezultatais. Tai man padėjo įgyti pasitikėjimo matavimų sistema, kurią naudojau eksperimentuose, kurių rezultatai buvo mažiau nuspėjami. Panašiai šis straipsnis naudoja „hprof“ profiliavimo įrankį, kad ištirtų tris paprastas valdymo programas, kurioms būdingi trys aukščiau išvardyti įprasti probleminiai veiksmai. Nors „hprof“ nėra toks patogus naudoti, kaip kai kurie rinkoje esantys komerciniai įrankiai, „hprof“ yra įtrauktas į „Java 2 JDK“ ir, kaip parodysiu, gali veiksmingai diagnozuoti šį elgesį.

Paleisti su hprof

Vykdyti programą su „hprof“ yra lengva. Tiesiog iškvieskite „Java“ vykdymo laiką naudodami šią komandinės eilutės parinktį, kaip aprašyta „Java“ programų paleidimo priemonės JDK įrankio dokumentacijoje:

java -Xrunhprof [: pagalba] [: =, ...] „MyMainClass“ 

Papildomų galimybių sąrašą galima rasti naudojant [: pagalba] parodyta parinktis. Šio straipsnio pavyzdžius sugeneravau naudodamas „JDK 1.3-RC1“, skirtą „Linux“, „Blackdown“ prievadą su šia paleidimo komanda:

java -classic -Xrunhprof: krūva = svetainės, procesorius = pavyzdžiai, gylis = 10, monitorius = y, siūlas = y, doe = y MemoryLeak 

Šiame sąraše paaiškinama kiekvieno ankstesnėje komandoje naudojamo papildomo pasirinkimo funkcija:

  • krūva = svetainės: Liepia „hprof“ generuoti kamino pėdsakus, nurodančius, kur buvo skirta atmintis
  • cpu = mėginiai: Liepia „hprof“ naudoti statistinę atranką norint nustatyti, kur CPU praleidžia laiką
  • gylis = 10: Liepia hprof rodyti kamino pėdsakus ne daugiau kaip 10 lygių gylyje
  • monitorius = y: Liepia „hprof“ generuoti informaciją apie varžybų monitorius, naudojamus sinchronizuoti kelių gijų darbą
  • siūlas = y: Liepia „hprof“ nustatyti gijas pėdsakuose
  • doe = y: Liepia „hprof“ išvesties metu susikurti profiliavimo duomenis

Jei naudojate JDK 1.3, turite išjungti numatytąjį „HotSpot“ kompiliatorių naudodami -klasikinis variantą. „HotSpot“ turi savo profilį, kurį iškviečia per -Xprof parinktis, kuri naudoja išvesties formatą, kitokį nei aprašysiu čia.

Vykdydami programą naudodami „hprof“, paliksite vadinamą failą java.hprof.txt savo darbo kataloge; Šiame faile yra profiliavimo informacija, surinkta vykdant jūsų programą. Be to, bet kuriuo metu, kai programa veikia, galite sukurti sąvartyną paspausdami „Ctrl- \“ „Java“ konsolės lange „Unix“ arba „Ctrl-Break“ sistemoje „Windows“.

Hprof išvesties failo anatomija

„Hprof“ išvesties faile yra skyriai, kuriuose aprašomos įvairios profiliuotos „Java“ programos charakteristikos. Jis prasideda antrašte, apibūdinančia jos formatą, kurį, antraštės teigimu, galima keisti be išankstinio įspėjimo.

Išvesties failo „Thread“ ir „Trace“ sekcijos padeda išsiaiškinti, kokios gijos buvo aktyvios, kai programa vykdė ir ką jie darė. Skyriuje „Gijos“ pateikiamas visų pradėtų ir nutrauktų gijų sąrašas programos metu. Skyriuje „Pėdsakas“ yra sunumeruotų kamino pėdsakų sąrašas kai kurioms gijoms. Šie kamino pėdsakų numeriai yra kryžminiai nuorodos kituose failų skyriuose.

Skyrius „Kaupo kaupimas“ ir „Svetainės“ padeda analizuoti atminties naudojimą. Priklausomai nuo krūva suboption, kurį pasirenkate paleidę virtualią mašiną (VM), galite gauti visų gyvų objektų „Java“ kaupe (krūva = sąvartynas) ir (arba) sutvarkytą paskirstymo vietų sąrašą, kuriame nustatomi labiausiai paskirstyti objektai (krūva = svetainės).

Procesoriaus pavyzdžiai ir CPU laiko skyriai padeda suprasti procesoriaus naudojimą; skyrius, kurį gaunate, priklauso nuo jūsų procesorius suboption (cpu = mėginiai arba cpu = laikas). CPU pavyzdžiai pateikia statistinį vykdymo profilį. Procesoriaus laikas apima matavimus, kiek kartų buvo iškviestas nurodytas metodas ir kiek laiko užtruko kiekvienas metodas.

Skyriai „Monitoriaus laikas“ ir „Monitor Dump“ padeda suprasti, kaip sinchronizavimas veikia jūsų programos našumą. Stebėjimo laikas rodo, kiek laiko jūsų gijos patiria ginčą dėl užrakintų išteklių. „Monitor Dump“ yra šiuo metu naudojamų monitorių momentinė nuotrauka. Kaip pamatysite, „Monitor Dump“ yra naudinga norint rasti aklavietes.

Diagnozuokite atminties nuotėkį

„Java“ atminties nutekėjimą apibrėžiu kaip (paprastai) netyčinį nesugebėjimą nukreipti atmestus objektus, kad šiukšlių surinkėjas negalėtų susigrąžinti jų naudojamos atminties. Atminties nuotėkis 1 sąrašo programa yra paprasta:

Sąrašas 1. „MemoryLeak“ programa

01 importuoti java.util.Vector; 02 03 viešoji klasė „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“ = naujas „ConsumerContainer“ (); 11 12 while (objectHolder.size () <MAX_CONSUMERS) {13 System.out.println ("Objekto paskirstymas" + 14 Integer.toString (objectHolder.size ()) 15); 16 objectHolder.add (naujas „MemoryConsumer“); 17 pabandykite {18 Thread.currentThread (). Sleep (SLEEP_BETWEEN_ALLOCS); 19} catch (InterruptedException ie) {20 // Nedaryti nieko. 21} 22} // o. 23} // pagrindinis. 24 25} // „MemoryLeak“ pabaiga. 26 27 / ** Pavadinta konteinerių klasė, skirta objektų nuorodoms laikyti. * / 28 klasės „ConsumerContainer“ pratęsia „Vector“ {} 29 30 / ** klasę, kuri sunaudoja fiksuotą atminties kiekį. * / 31 klasės „MemoryConsumer“ {32 public static final int MEMORY_BLOCK = 1024; 33 viešasis baitas [] memoryHoldingArray; 34 35 MemoryConsumer () {36 memoryHoldingArray = naujas baitas [ATMINTI_BLOKA]; 37} 38} // Baigti „MemoryConsumer“. 

Kai programa veikia, ji sukuria „ConsumerContainer“ tada pradeda kurti ir pridėti Atminties vartotojas objektai yra mažiausiai 1 KB dydžio „ConsumerContainer“ objektas. Laikydami prieinamus objektus, jų negalima pasiekti atliekų surinkimui, imituojant atminties nutekėjimą.

Pažvelkime į pasirinktas profilio failo dalis. Kelios pirmosios „Svetainių“ eilutės aiškiai parodo, kas vyksta:

SITES BEGIN 40964 1 81880 10 1996 [L; 3 0,38% 98,07% 40000 10000 40000 10000 1994 „MemoryConsumer“ 4 0,16% 98,23% 16388 1 16388 1 1295 [C 5 0,16% 98,38% 16388 1 16388 1 1304 [C ... 

Yra 10 000 tipo objektų baitas [] ([B kalbant VM), taip pat 10 000 Atminties vartotojas objektai. Baitų masyvai užima 10 280 000 baitų, taigi, matyt, virš kiekvienos masyvo sunaudotų neapdorotų baitų yra pridėtinės išlaidos. Kadangi paskirstytų objektų skaičius yra lygus gyvų objektų skaičiui, galime daryti išvadą, kad nė vienas iš šių objektų negalėjo būti surinktas šiukšlių. Tai atitinka mūsų lūkesčius.

Kitas įdomus dalykas: pranešama, kad atmintį sunaudojo Atminties vartotojas objektai neapima baitų masyvų sunaudotos atminties. Tai rodo, kad mūsų profiliavimo įrankis atskleidžia ne hierarchinius izoliavimo ryšius, o statistiką pagal klases. Tai svarbu suprasti naudojant „hprof“, norint tiksliai nustatyti atminties nutekėjimą.

Iš kur tie nesandarūs baitų masyvai? Atkreipkite dėmesį, kad Atminties vartotojas objektai ir baitų masyvai nurodo pėdsakus 1994 ir 1995 sekančiame sekimo skyriuje. Štai, šitie pėdsakai mums sako, kad Atminties vartotojas objektai buvo sukurti Atminties nuotėkis klasės pagrindinis () metodas ir kad baitų masyvai buvo sukurti konstruktoriuje (() metodas VM kalbėti). Radome atminties nutekėjimą, linijų numerius ir visa kita:

TRACE 1994: (gija = 1) „MemoryLeak.main“ („MemoryLeak.java:16“) TRACE 1995: [gija = 1] „MemoryConsumer“. („MemoryLeak.java:36“) „MemoryLeak.main“ („MemoryLeak.java:16“) 

Diagnozuokite CPU hog

2 sąraše a „BusyWork“ klasėje kiekvienas gijos iškvietimas yra metodas, reguliuojantis sriegio veikimą, keičiant jo miego laiką tarp intensyvaus procesoriaus skaičiavimų.

Sąrašas 2. „CPUHog“ programa

01 / ** Pagrindinė kontrolinio bandymo klasė. * / 02 viešosios klasės „CPUHog“ {03 public static void main (String [] args) {04 05 „Thread slouch“, „WorkingStiff“, darboholikas; 06 slouch = new Slouch (); 07 workingStiff = naujas WorkingStiff (); 08 darboholikas = naujas darboholikas (); 09 10 slouch.start (); 11 darboStiff.start (); 12 darboholikas.pradėti (); 13} 14} 15 16 / ** Žemas procesoriaus panaudojimo būdas. * / 17 klasės „Slouch“ pratęsia siūlą {18 public Slouch () {19 super („Slouch“); 20} 21 viešas niekinis bėgimas () {22 BusyWork.slouch (); 23} 24} 25 26 / ** Vidutinio procesoriaus naudojimo gija. * / 27 klasės „WorkingStiff“ išplečia siūlą {28 public WorkingStiff () {29 super („WorkingStiff“); 30} 31 viešas niekinis vykdymas () {32 BusyWork.workNormally (); 33} 34} 35 36 / ** didelio procesoriaus naudojimo gija. * / 37 klasės darboholikas pratęsia siūlą {38 public Workaholic () {39 super ("Workaholic"); 40} 41 public void run () {42 BusyWork.workTillYouDrop (); 43} 44} 45 46 / ** klasė su statiniais metodais, kad sunaudotų įvairius kiekius 47 * procesoriaus laiko. * / 48 klasės „BusyWork“ {49 50 public static int callCount = 0; 51 52 viešasis statinis tuštumas () {53 int SLEEP_INTERVAL = 1000; 54 „computeAndSleepLoop“ (SLEEP_INTERVAL); 55} 56 57 viešasis statinis niekinis darbasNormaliai () {58 int SLEEP_INTERVAL = 100; 59 „computeAndSleepLoop“ (SLEEP_INTERVAL); 60} 61 62 viešasis statinis tuštumas workTillYouDrop () {63 int SLEEP_INTERVAL = 10; 64 „computeAndSleepLoop“ (SLEEP_INTERVAL); 65} 66 67 privati ​​statinė tuštuma computeAndSleepLoop (int sleepInterval) {68 int MAX_CALLS = 10000; 69 while (callCount <MAX_CALLS) {70 computeAndSleep (sleepInterval); 71} 72} 73 74 privatus statinis tuštumas computeAndSleep (int sleepInterval) {75 int COMPUTATIONS = 1000; 76 dvigubas rezultatas; 77 78 // Apskaičiuokite. 79 callCount ++; 80 (int i = 0; i <KOMPIUTERIAI; i ++) {81 rezultatas = Math.atan (callCount * Math.random ()); 82} 83 84 // Miegas. 85 pabandykite {86 Thread.currentThread (). Sleep (sleepInterval); 87} catch (InterruptedException ie) {88 // Nieko nedaryti. 89} 90 91} // Baigti skaičiuoti ir miegoti. 92} // End BusyWork. 

Yra trys gijos - Darboholikas, „WorkingStiff“ir Slouch - kurių darbo etika skiriasi pagal dydį, sprendžiant pagal pasirinktą darbą. Išnagrinėkite profilio CPU pavyzdžių skyrių, parodytą žemiau. Trys aukščiausio rango pėdsakai rodo, kad procesorius didžiąją laiko dalį praleido apskaičiuodamas atsitiktinius skaičius ir lanko liestines, kaip mes tikėtumės:

CPU MĖGINIAI PRADŽIA (iš viso = 935) Antradienis rugsėjo 4 20:44:49 2001 m. Reitingo savikainos skaičiavimo atsekimo metodas 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.nextDouble 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 / Matematinis atsitiktinumas 18 0,11% 99,36% 1 2049 java / util / Random.nextDouble 19 0,11% 99,47% 1 2031 BusyWork.computeAndSleep 20 0.11% 99.57% 1 1530 sun / io / CharToByteISO8859_1.convert ... 

Atkreipkite dėmesį, kad skambučiai į BusyWork.computeAndSleep () metodas užima 8,13 proc., 0,43 proc. ir 0,11 proc Darboholikas, „WorkingStiff“ir Slouch siūlai, atitinkamai. Mes galime pasakyti, kurios gijos tai yra, ištyrę pėdsakus, nurodytus aukščiau esančiame CPU pavyzdžių skilties pėdsakų stulpelyje (4, 10 ir 19 rangai) šioje sekcijoje: