Programavimas

CPU naudojimo profiliavimas iš „Java“ programos

2002 m. Lapkričio 8 d

Klausimas: Kaip nustatyti procesoriaus naudojimą „Java“?

A: Taigi, čia yra geros ir blogos naujienos. Blogos naujienos yra tai, kad naudojant gryną „Java“ neįmanoma programiškai pateikti užklausų dėl procesoriaus naudojimo. Tam tiesiog nėra API. Siūloma alternatyva gali būti naudojama „Runtime.exec“ () Norėdami nustatyti JVM proceso ID (PID), iškvieskite išorinę, konkrečiai platformai skirtą komandą, panašią psir išanalizuokite savo produkciją pagal dominančią PID. Bet šis požiūris geriausiu atveju yra trapus.

Tačiau gera žinia ta, kad patikimą sprendimą galima pasiekti žengiant už „Java“ ribų ir parašius kelias C kodo eilutes, kurios integruojamos su „Java“ programa per „Java Native Interface“ (JNI). Toliau parodysiu, kaip lengva, sukūrus paprastą „JNI“ biblioteką „Win32“ platformai. Išteklių skyriuje yra nuoroda į biblioteką, kurią galite pritaikyti savo poreikiams, ir nukreipkite į kitas platformas.

Apskritai JNI yra šiek tiek sudėtinga naudoti. Tačiau kai skambinate tik viena kryptimi - iš „Java“ į gimtąjį kodą - ir bendraujate naudodami primityvius duomenų tipus, viskas lieka paprasta. JNI yra daug gerų nuorodų (žr. Šaltinius), todėl čia nepateikiu JNI mokymo programos; Aš tik apibūdinu savo įgyvendinimo žingsnius.

Aš pradedu kurdamas klasę com.vladium.utils.SystemInformation kuris deklaruoja savąjį metodą, kuris grąžina procesoriaus laiko milisekundžių skaičių, kurį iki šiol naudojo dabartinis procesas:

 public static native long getProcessCPUTime (); 

Aš naudoju „javah“ įrankį iš JDK, kad galėčiau sukurti šią C antraštę savo būsimam savam diegimui:

JNIEXPORT jlong ​​JNICALL Java_com_vladium_utils_SystemInformation_getProcessCPUTime (JNIEnv * env, jclass cls) 

Daugumoje „Win32“ platformų šį metodą galima įdiegti naudojant „GetProcessTimes“ () sistemos skambutis ir pažodžiui yra trys C kodo eilutės:

JNIEXPORT jlong ​​JNICALL Java_com_vladium_utils_SystemInformation_getProcessCPUTime (JNIEnv * env, jclass cls) {FILETIME creationTime, exitTime, kernelTime, userTime; „GetProcessTimes“ (s_currentProcess, & creationTime, & exitTime, & kernelTime, & userTime); grįžti (jlong) ((fileTimeToInt64 (& kernelTime) + fileTimeToInt64 (& userTime)) / (s_numberOfProcessors * 10000)); } 

Šis metodas prideda procesoriaus laiką, praleistą vykdant branduolį ir vartotojo kodą dabartinio proceso vardu, normalizuoja jį pagal procesorių skaičių ir konvertuoja rezultatą į milisekundes. fileTimeToInt64 () yra pagalbinė funkcija, kuri paverčia FILETIME struktūra iki 64 bitų sveiko skaičiaus ir s_currentProcess ir s_numberOfProcessors yra visuotiniai kintamieji, kuriuos galima patogiai inicijuoti JNI metodu, kuris iškviečiamas vieną kartą, kai JVM įkelia gimtąją biblioteką:

statinis HANDLE s_currentProcess; static int s_numberOfProcessors; JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, negaliojantis * rezervuotas) {SYSTEM_INFO systemInfo; s_currentProcess = GetCurrentProcess (); „GetSystemInfo“ (& systemInfo); s_numberOfProcessors = systemInfo.dwNumberOfProcessors; grąžinti JNI_VERSION_1_2; } 

Atkreipkite dėmesį, kad jei įgyvendinsite getProcessCPUTime () „Unix“ platformoje greičiausiai naudosite skrandis sistemos skambutis kaip pradinis taškas.

Grįžimas į „Java“, gimtosios bibliotekos įkėlimas (silib.dll Win32) geriausiai galima pasiekti naudojant statinį inicializatorių Sistemos informacija klasė:

 privati ​​statinė galutinė eilutė SILIB = "silib"; statinis {pabandykite {System.loadLibrary (SILIB); } catch (UnsatisfiedLinkError e) {System.out.println ("native lib" "+ SILIB +" 'nerastas' java.library.path ': "+ System.getProperty (" java.library.path ")); mesti e; // permetimas}} 

Prisimink tai „getProcessCPUTime“ () pateikia CPU laiką, sunaudotą nuo JVM proceso sukūrimo. Šie duomenys savaime nėra ypač naudingi profiliuojant. Man reikia daugiau naudingų „Java“ metodų, norint įrašyti duomenų momentines nuotraukas įvairiais laikais ir pranešti apie procesoriaus naudojimą tarp bet kokių dviejų laiko taškų:

 public static final class CPUUsageSnapshot {private CPUUsageSnapshot (ilgas laikas, ilgas CPUTime) {m_time = laikas; m_CPUTime = CPUTime; } public final ilgas m_time, m_CPUTime; } // įdėtos klasės viešojo statinio CPUUsageSnapshot makeCPUUsageSnapshot () pabaiga {grąžinti naują CPUUsageSnapshot (System.currentTimeMillis (), getProcessCPUTime ()); } public static double getProcessCPUUsage (CPUUsageSnapshot start, CPUUsageSnapshot end) {return ((double) (end.m_CPUTime - start.m_CPUTime)) / (end.m_time - start.m_time); } 

„CPU monitoriaus API“ yra beveik paruošta naudoti! Kaip paskutinį prisilietimą sukuriu pavienių siūlų klasę, CPUUsageThread, kuris automatiškai reguliariai (0,5 sekundės pagal numatytuosius nustatymus) daro duomenų momentines nuotraukas ir praneša apie jas procesoriaus naudojimo įvykių klausytojų rinkiniui (žinomas „Observer“ modelis). CPUmon klasė yra demonstracinis klausytojas, kuris tiesiog spausdina procesoriaus naudojimą System.out:

 public static void main (String [] args) išmeta išimtį {if (args.length == 0) mest naują IllegalArgumentException ("naudojimas: CPUmon"); CPUUsageThread monitor = CPUUsageThread.getCPUThreadUsageThread (); CPUmon _this = naujas CPUmon (); „Class app“ = „Class.forName“ (argumentai [0]); Metodas appmain = app.getMethod ("main", nauja klasė [] {String []. Class}); String [] appargs = new String [argumentų ilgis - 1]; System.arraycopy (args, 1, appargs, 0, appargs.length); monitor.addUsageEventListener (_tai); monitor.start (); appmain.invoke (null, naujas objektas [] {appargs}); } 

Be to, CPUmon.main () „apgaubia“ kitą pagrindinę „Java“ klasę, kurios vienintelis tikslas yra pradėti CPUUsageThread prieš paleidžiant pradinę programą.

Kaip demonstraciją bėgau CPUmon su „SwingSet2 Swing“ demonstracine versija iš JDK 1.3.1 (nepamirškite įdiegti silib.dll į vietą, kurią apima KELIS OS aplinkos kintamasis arba java.library.path „Java“ ypatybė):

> java -Djava.library.path =. -cp silib.jar; (mano JDK diegimo aplankas) \ demo \ jfc \ SwingSet2 \ SwingSet2.jar CPUmon SwingSet2 [PID: 339] Procesoriaus naudojimas: 46,8% [PID: 339] Procesoriaus naudojimas: 51,4% [PID: 339] CPU naudojimas: 54,8% (įkeliant demonstracinė versija naudoja beveik 100% vieno iš dviejų mano kompiuterio procesorių) ... [PID: 339] Procesoriaus naudojimas: 46,8% [PID: 339] Procesoriaus naudojimas: 0% [PID: 339] Procesoriaus naudojimas: 0% (demonstracinė versija baigė įkelti visas savo plokštes ir dažniausiai neveikia) ... [PID: 339] Procesoriaus naudojimas: 100% [PID: 339] Procesoriaus naudojimas: 98,4% [PID: 339] CPU naudojimas: 97% (perėjau į „ColorChooserDemo“ skydą, kuriame vykdyta daug procesoriaus atliekanti animacija, naudojanti abu mano procesorius) ... [PID: 339] Procesoriaus naudojimas: 81,4% [PID: 339] Procesoriaus naudojimas: 50% [PID : 339] Procesoriaus naudojimas: 50% (aš naudoju „Windows NT“ užduočių tvarkyklę norėdamas pakoreguoti „java“ procesoriaus giminingumą, kad būtų naudojamas vienas procesorius) ... 

Žinoma, tuos pačius naudojimo numerius galiu žiūrėti per užduočių tvarkytuvę, tačiau esmė ta, kad dabar turiu programinį būdą įrašyti tuos pačius duomenis. Tai pravers atliekant ilgai trunkančius bandymus ir serverio programų diagnostiką. Visa biblioteka (galima rasti šaltiniuose) prideda keletą kitų naudingų vietinių metodų, įskaitant vieną, skirtą proceso PID gauti (integravimui su išoriniais įrankiais).

Vladimiras Roubtsovas programavo įvairiomis kalbomis daugiau nei 12 metų, įskaitant „Java“ nuo 1995 m. Šiuo metu jis kuria įmonės programinę įrangą kaip „Trilogy“ vyresnysis kūrėjas Ostine, Teksase. Koduodamas savo malonumui, Vladimiras kuria programinės įrangos įrankius, pagrįstus „Java“ baito kodu arba šaltinio kodo instrumentais.

Sužinokite daugiau apie šią temą

  • Atsisiųskite visą biblioteką, pridedamą prie šio straipsnio

    //images.techhive.com/downloads/idge/imported/article/jvw/2002/11/01-qa-1108-cpu.zip

  • JNI specifikacijos ir pamokos

    //java.sun.com/j2se/1.4/docs/guide/jni/index.html

  • Gerą JNI apžvalgą rasite Stuart Dabbs Halloway Komponentų kūrimas „Java“ platformai (Addison-Wesley, 2001 m. Gruodžio mėn .; ISBN0201753065)

    //www.amazon.com/exec/obidos/ASIN/0201753065/javaworld

  • Jesperas Gortzas dalyje „Java Tip 92Use JVM Profiler Interface for Precimime Timing“ tikslią analizę pateikia alternatyvią procesoriaus profiliavimo kryptį. (Tačiau norint naudoti JVMPI reikia daugiau darbo, norint apskaičiuoti procesoriaus naudojimą visam procesui, palyginti su šio straipsnio sprendimu)

    //www.javaworld.com/javaworld/javatips/jw-javatip92.html

  • Žr „Java“ klausimai ir atsakymai viso klausimų ir atsakymų katalogo rodyklės puslapis

    //www.javaworld.com/columns/jw-qna-index.shtml

  • Norėdami gauti daugiau nei 100 įžvalgių „Java“ patarimų, apsilankykite „JavaWorld“s „Java“ patarimai rodyklės puslapis

    //www.javaworld.com/columns/jw-tips-index.shtml

  • Naršykite „Core Java“ skyrius „JavaWorld“s aktuali rodyklė

    //www.javaworld.com/channel_content/jw-core-index.shtml

  • Gaukite daugiau klausimų į mūsų atsakymus „Java“ pradedantysis diskusija

    //forums.devworld.com/webx?50@@.ee6b804

  • Prisiregistruokite „JavaWorld“nemokami savaitiniai el. pašto naujienlaiškiai

    //www.javaworld.com/subscribe

  • Rasite daugybę su IT susijusių straipsnių iš mūsų seserų leidinių .net

Šią istoriją „Procesoriaus naudojimo profiliavimas iš„ Java “programos“ iš pradžių paskelbė „JavaWorld“.

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