Programmēšana

Diagnosticējiet bieži sastopamas rprime problēmas ar hprof

Atmiņas noplūde un strupceļš, kā arī CPU cūkas, ak vai! Java lietojumprogrammu izstrādātāji bieži sastopas ar šīm izpildlaika problēmām. Tie var būt īpaši biedējoši sarežģītā lietojumprogrammā ar vairākiem pavedieniem, kas iziet cauri simtiem tūkstošu koda rindu - lietojumprogrammai, kuru nevarat nosūtīt, jo tā pieaug atmiņā, kļūst neaktīva vai aplaupa vairāk CPU ciklu, nekā vajadzētu.

Nav noslēpums, ka Java profilēšanas rīkiem ir bijis tāls ceļš, lai panāktu viņu aizstājējvalodas kolēģus. Tagad pastāv daudzi spēcīgi rīki, kas mums palīdz izsekot vainīgos šo kopīgo problēmu pamatā. Bet kā attīstīt pārliecību par spēju efektīvi izmantot šos rīkus? Galu galā jūs izmantojat rīkus, lai diagnosticētu sarežģītu uzvedību, kuru nesaprotat. Lai sarežģītu jūsu stāvokli, rīku sniegtie dati ir pietiekami sarežģīti, un informācija, kuru meklējat vai meklējat, ne vienmēr ir skaidra.

Saskaroties ar līdzīgām problēmām manā iepriekšējā iemiesojumā kā eksperimentālais fiziķis, es izveidoju kontroles eksperimentus ar paredzamiem rezultātiem. Tas man palīdzēja iegūt pārliecību par mērījumu sistēmu, kuru izmantoju eksperimentos, kas radīja mazāk paredzamus rezultātus. Līdzīgi šajā rakstā tiek izmantots profilēšanas rīks hprof, lai pārbaudītu trīs vienkāršas vadības lietojumprogrammas, kurās ir trīs iepriekš uzskaitītās kopīgās problēmu uzvedības. Lai arī hprof nav tik lietotājam draudzīgs kā daži komerciālie rīki tirgū, Java 2 JDK ir iekļauts hprof, un, kā es parādīšu, tas var efektīvi diagnosticēt šo uzvedību.

Palaist ar hprof

Programmas vadīšana ar hprof ir vienkārša. Vienkārši izsauciet Java izpildlaiku ar šādu komandrindas opciju, kā aprakstīts Java lietojumprogrammu palaidēja JDK rīka dokumentācijā:

java -Xrunhprof [: palīdzība] [: =, ...] MyMainClass 

Apakšpakalpojumu saraksts ir pieejams vietnē [: palīdzība] parādīta opcija. Šajā rakstā es izveidoju piemērus, izmantojot JDK 1.3-RC1 operētājsistēmas Linux Blackdown portu ar šādu palaišanas komandu:

java -classic -Xrunhprof: kaudze = vietnes, cpu = paraugi, dziļums = 10, monitors = y, pavediens = y, doe = y MemoryLeak 

Šajā sarakstā ir paskaidrota katra iepriekšējā komandā izmantotā apakšopcijas funkcija:

  • kaudze = vietnes: Liek hprof ģenerēt kaudzes pēdas, norādot, kur tika piešķirta atmiņa
  • cpu = paraugi: Liek hprof izmantot statistisko izlasi, lai noteiktu, kur CPU pavada laiku
  • dziļums = 10: Liek hprofam parādīt kaudzes pēdas ne vairāk kā 10 līmeņu dziļumā
  • monitors = y: Liek hprof ģenerēt informāciju par konkurences monitoriem, kurus izmanto, lai sinhronizētu vairāku pavedienu darbu
  • pavediens = y: Liek hprofam identificēt pavedienus kaudzes pēdās
  • stirna = y: Liek hprof, lai pēc izejas izveidotu profilēšanas datu izgāztuvi

Ja izmantojat JDK 1.3, jums ir jāizslēdz noklusējuma HotSpot kompilators ar -klasisks opcija. HotSpot ir savs profilētājs, kuru izsauc caur -Xprof opcija, kas izmanto izvades formātu, kas atšķiras no šeit aprakstītā.

Palaidot programmu ar hprof, paliks fails ar nosaukumu java.hprof.txt savā darba direktorijā; Šis fails satur profilēšanas informāciju, kas savākta, kamēr darbojas jūsu programma. Varat arī ģenerēt izgāztuvi jebkurā laikā, kamēr programma darbojas, nospiežot Ctrl- \ Java konsoles logā Unix vai Ctrl-Break sistēmā Windows.

Hprof izvades faila anatomija

Hprof izvades failā ir sadaļas, kas apraksta dažādas profilētās Java programmas īpašības. Tas sākas ar galveni, kas apraksta tā formātu, kuru, kā apgalvo galvene, var mainīt bez iepriekšēja brīdinājuma.

Izvades faila sadaļas Thread and Trace palīdz noskaidrot, kādi pavedieni bija aktīvi, kad programma darbojās, un ko viņi darīja. Sadaļā Thread ir saraksts ar visiem pavedieniem, kas sākti un pārtraukti programmas darbības laikā. Sadaļā Trace ir iekļauts dažu pavedienu numurētu kaudzes izsekojumu saraksts. Šie kaudzes izsekošanas numuri ir savstarpēji saistīti ar citām failu sadaļām.

Sadaļas Kupu izgāšana un vietnes palīdz analizēt atmiņas lietojumu. Atkarībā no kaudze apakšoptionu, kuru izvēlaties, startējot virtuālo mašīnu (VM), jūs varat iegūt visus dzīvos objektus Java kaudzē (kaudze = izgāzt) un / vai sakārtotu piešķiršanas vietņu sarakstu, kas identificē visvairāk piešķirtos objektus (kaudze = vietnes).

Procesora paraugi un CPU laika sadaļas palīdz saprast CPU izmantošanu; sadaļa, kuru saņemat, ir atkarīga no jūsu Procesors suboption (cpu = paraugi vai cpu = laiks). CPU paraugi nodrošina statistikas izpildes profilu. CPU laiks ietver mērījumus, cik reizes tika izsaukta dotā metode un cik ilgs laiks bija katras metodes izpildei.

Sadaļas Monitora laiks un Monitora izgāšana palīdz saprast, kā sinhronizācija ietekmē jūsu programmas veiktspēju. Monitora laiks parāda, cik daudz laika jūsu pavedieni piedzīvo strīdu par bloķētiem resursiem. Monitor Dump ir pašlaik izmantoto monitoru momentuzņēmums. Kā redzēsit, Monitor Dump ir noderīgs, lai atrastu strupceļus.

Diagnosticējiet atmiņas noplūdi

Java valodā es atmiņas noplūdi definēju kā (parasti) nejaušu nespēju novirzīt izmestos objektus, lai atkritumu savācējs nevarētu atgūt viņu izmantoto atmiņu. The Atmiņas noplūde 1. saraksta programma ir vienkārša:

Uzskaitīšana 1. MemoryLeak programma

01 importēt java.util.Vector; 02 03 public class 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 = new ConsumerContainer (); 11 12 while (objectHolder.size () <MAX_CONSUMERS) {13 System.out.println ("Objekta piešķiršana" + 14 Integer.toString (objectHolder.size ()) 15); 16 objectHolder.add (jauns MemoryConsumer ()); 17 izmēģiniet {18 Thread.currentThread (). Sleep (SLEEP_BETWEEN_ALLOCS); 19} catch (InterruptedException ie) {20 // Neko nedarīt. 21} 22} // kamēr. 23} // galvenais. 24 25} // MemoryLeak beigas. 26 27 / ** Nosaukta konteineru klase objektu atsauču glabāšanai. * / 28 klases ConsumerContainer paplašina Vector {} 29 30 / ** klasi, kas patērē noteiktu atmiņas daudzumu. * / 31 klases MemoryConsumer {32 public static final int MEMORY_BLOCK = 1024; 33 publiskais baits [] memoryHoldingArray; 34 35 MemoryConsumer () {36 memoryHoldingArray = jauns baits [MEMORY_BLOCK]; 37} 38} // Beigt atmiņu. 

Kad programma darbojas, tā izveido ConsumerContainer objektu, pēc tam sāk veidot un pievienot AtmiņaPatērētājs objektus, kuru lielums ir vismaz 1 KB ConsumerContainer objekts. Turot objektus pieejamus, tie nav pieejami atkritumu savākšanai, imitējot atmiņas noplūdi.

Apskatīsim izvēlētās profila faila daļas. Sadaļas Vietnes pirmās pāris rindas skaidri parāda, kas notiek:

SITES BEGIN (pasūtījis tiešie baiti) Pirmdien, 3. septembrī 19:16:29 2001 procenti tiešraidē sadalītu kaudzes klases rangs sevis uzkrāšanas baiti objs baiti objs izsekošanas nosaukums 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 MemoryConsumer 4 0,16% 98,23% 16388 1 16388 1 1295 [C 5 0,16% 98,38% 16388 1 16388 1 1304 [C ... 

Ir 10 000 veida objektu baits [] ([B VM runā), kā arī 10 000 AtmiņaPatērētājs objektiem. Baitu masīvi aizņem 10 280 000 baitus, tāpēc acīmredzot virs neapstrādātiem baitiem, kurus patērē katrs masīvs, ir pieskaitāmās izmaksas. Tā kā piešķirto objektu skaits ir vienāds ar dzīvo objektu skaitu, mēs varam secināt, ka nevienu no šiem objektiem nevar savākt atkritumus. Tas atbilst mūsu cerībām.

Vēl viens interesants aspekts: atmiņa, ko ziņo patērējusi AtmiņaPatērētājs objekti neietver atmiņu, ko patērē baitu masīvi. Tas parāda, ka mūsu profilēšanas rīks neatklāj hierarhiskas ierobežošanas attiecības, bet gan statistiku pa klasēm. Tas ir svarīgi saprast, lietojot hprof, lai precīzi noteiktu atmiņas noplūdi.

No kurienes radās šie noplūdušie baitu bloki? Ievērojiet, ka AtmiņaPatērētājs objektu un baitu masīvu atsauces pēdas 1994 un 1995 nākamajā sadaļā Trace. Lūk, šīs pēdas mums saka, ka AtmiņaPatērētājs objekti tika izveidoti Atmiņas noplūde klases galvenais () metodi un ka baitu masīvi tika izveidoti konstruktorā (() metode VM-runā). Mēs esam atraduši atmiņas noplūdi, līniju numurus un visu:

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

Diagnosticējiet CPU cūku

2. sarakstā a BusyWork klasei katra pavediena izsaukšana ir metode, kas regulē pavediena darbību, mainot tā miega laiku starp procesoriem intensīvu aprēķinu veikšanu:

Saraksts 2. CPUHog programma

01 / ** Kontroles testa galvenā klase. * / 02 public class CPUHog {03 public static void main (String [] args) {04 05 Vītnes slinkums, workingStiff, darbaholiķis; 06 slouch = jauns Slouch (); 07 workingStiff = jauns WorkingStiff (); 08 darbaholiķis = jaunais darbaholiķis (); 09 10 slouch.start (); 11 darbaStiff.start (); 12 darbaholiķis.sākt (); 13} 14} 15 16 / ** Zema procesora izmantošanas pavediens. * / 17 klases Slouch paplašina Thread {18 public Slouch () {19 super ("Slouch"); 20} 21 publiska anulēšana () {22 BusyWork.slouch (); 23} 24} 25 26 / ** Vidēja procesora izmantošanas pavediens. * / 27 klases WorkingStiff paplašina pavedienu {28 public WorkingStiff () {29 super ("WorkingStiff"); 30} 31 publiska anulēšana () {32 BusyWork.workNormally (); 33} 34} 35 36 / ** augsta CPU izmantošanas pavediens. * / 37 klases darbaholiķis pagarina pavedienu {38 publiskais darbaholiķis () {39 super ("darbaholiķis"); 40} 41 publiska anulēšana () {42 BusyWork.workTillYouDrop (); 43} 44} 45 46 / ** Klase ar statiskām metodēm, lai patērētu dažādas summas 47 * no procesora laika. * / 48 klases BusyWork {49 50 publiskais statiskais int callCount = 0; 51 52 publiskais statiskais tukšums () {53 int SLEEP_INTERVAL = 1000; 54 computeAndSleepLoop (SLEEP_INTERVAL); 55} 56 57 publiskais statiskais tukšumsNormally () {58 int SLEEP_INTERVAL = 100; 59 computeAndSleepLoop (SLEEP_INTERVAL); 60} 61 62 publiskais statiskais tukšums workTillYouDrop () {63 int SLEEP_INTERVAL = 10; 64 computeAndSleepLoop (SLEEP_INTERVAL); 65} 66 67 privāta statiska tukšuma computeAndSleepLoop (int sleepInterval) {68 int MAX_CALLS = 10000; 69 while (callCount <MAX_CALLS) {70 computeAndSleep (sleepInterval); 71} 72} 73 74 privāta statiska tukšuma computeAndSleep (int sleepInterval) {75 int COMPUTATIONS = 1000; 76 dubultā rezultāts; 77 78 // Aprēķināt. 79 callCount ++; 80 (int i = 0; i <Skaitļi; i ++) {81 rezultāts = Math.atan (callCount * Math.random ()); 82} 83 84 // Miega režīms 85 izmēģiniet {86 Thread.currentThread (). Sleep (sleepInterval); 87} catch (InterruptedException ie) {88 // Neko nedarīt. 89} 90 91} // Beigt aprēķinātAndSleep. 92} // Beigt aizņemto darbu. 

Ir trīs pavedieni - Darbaholiķis, WorkingStiff, un Slaucīt - kuru darba ētika atšķiras pēc lieluma pakāpēm, vērtējot pēc viņu izvēlētā darba. Pārbaudiet profila CPU paraugu sadaļu, kas parādīta zemāk. Trīs visaugstāk novērtētās pēdas rāda, ka centrālais procesors lielāko daļu laika pavadīja, aprēķinot nejaušus skaitļus un loka pieskares, kā mēs varētu sagaidīt:

Sākas CPU PARAUGI (kopā = 935), otrdien, 4. septembrī 20:44:49 2001. gada rangu pašapkalpošanās skaita izsekošanas metode 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. gada 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.nextDouble 19 0,11% 99,47% 1 2031 BusyWork.computeAndSleep 20 0.11% 99.57% 1 1530 sun / io / CharToByteISO8859_1.convert ... 

Ņemiet vērā, ka zvani uz BusyWork.computeAndSleep () metode aizņem 8,13 procentus, 0,43 procentus un 0,11 procentus Darbaholiķis, WorkingStiff, un Slaucīt attiecīgi pavedieni. Mēs varam pateikt, kuras tēmas ir, pārbaudot pēdas, uz kurām atsaucas CPU paraugu sadaļas augšējā slejā (4., 10. un 19. rindā) šajā sadaļā Trace:

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