Programmering

Profilering av CPU-bruk fra et Java-program

8. november 2002

Spørsmål: Hvordan bestemmer du CPU-bruk i Java?

EN: Så her er de gode nyhetene og de dårlige nyhetene. Den dårlige nyheten er at det umulig å bruke Java er programmatisk spørring etter CPU-bruk. Det er rett og slett ingen API for dette. Et foreslått alternativ kan bruke Runtime.exec () for å bestemme JVMs prosess-ID (PID), ring en ekstern, plattformsspesifikk kommando som ps, og analyser produksjonen for PID av interesse. Men denne tilnærmingen er i beste fall skjøre.

Den gode nyheten er imidlertid at en pålitelig løsning kan oppnås ved å gå utenfor Java og skrive noen få C-kodelinjer som integreres med Java-applikasjonen via Java Native Interface (JNI). Jeg viser nedenfor hvor enkelt det er ved å lage et enkelt JNI-bibliotek for Win32-plattformen. Ressursdelen inneholder en lenke til biblioteket du kan tilpasse for dine egne behov og porter til andre plattformer.

Generelt er JNI noe komplisert å bruke. Når du bare ringer i en retning - fra Java til innfødt kode - og kommuniserer ved hjelp av primitive datatyper, er ting fortsatt enkle. Det er mange gode referanser (se Ressurser) på JNI, så jeg gir ikke en JNI-opplæring her; Jeg skisserer bare implementeringstrinnene mine.

Jeg begynner med å lage en klasse com.vladium.utils.SystemInformation som erklærer en innfødt metode, som returnerer antall millisekunder CPU-tid brukt av den nåværende prosessen så langt:

 offentlig statisk innfødt lang getProcessCPUTime (); 

Jeg bruker javah-verktøyet fra JDK til å produsere følgende C-overskrift for min fremtidige innfødte implementering:

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

På de fleste Win32-plattformer kan denne metoden implementeres ved hjelp av GetProcessTimes () systemanrop og er bokstavelig talt tre linjer med C-kode:

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); retur (jlong) ((fileTimeToInt64 (& kernelTime) + fileTimeToInt64 (& userTime)) / (s_numberOfProcessors * 10000)); } 

Denne metoden legger til CPU-tid brukt på å utføre kjerne og brukerkode på vegne av den nåværende prosessen, normaliserer den etter antall prosessorer og konverterer resultatet til millisekunder. De fileTimeToInt64 () er en hjelperfunksjon som konverterer FILETID struktur til et 64-biters heltall, og s_currentProcess og s_numberOfProcessors er globale variabler som enkelt kan initialiseres i en JNI-metode som kalles en gang når JVM laster det opprinnelige biblioteket:

statisk HÅNDTER s_currentProcess; statisk int s_numberOfProcessors; JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, ugyldig * reservert) {SYSTEM_INFO systemInfo; s_currentProcess = GetCurrentProcess (); GetSystemInfo (& systemInfo); s_numberOfProcessors = systemInfo.dwNumberOfProcessors; returner JNI_VERSION_1_2; } 

Merk at hvis du implementerer getProcessCPUTime () på en Unix-plattform, vil du sannsynligvis bruke getrusage systemanrop som utgangspunkt.

Å komme tilbake til Java, laste det opprinnelige biblioteket (silib.dll på Win32) oppnås best via den statiske initialisereren i Systeminformasjon klasse:

 privat statisk slutt String SILIB = "silib"; statisk {prøv {System.loadLibrary (SILIB); } fange (UnsatisfiedLinkError e) {System.out.println ("native lib '" + SILIB + "' ikke funnet i 'java.library.path':" + System.getProperty ("java.library.path")); kaste e; // re-throw}} 

Noter det getProcessCPUTime () returnerer CPU-tiden som ble brukt siden opprettelsen av JVM-prosessen. I seg selv er disse dataene ikke spesielt nyttige for profilering. Jeg trenger flere Java-metoder for å registrere øyeblikksbilder av data til forskjellige tider og rapportere CPU-bruk mellom to tidspunkter:

 offentlig statisk sluttklasse CPUUsageSnapshot {private CPUUsageSnapshot (lang tid, lang CPUTime) {m_time = tid; m_CPUTime = CPUTime; } offentlig finale lang m_tid, m_CPUTime; } // slutten av nestet klasse offentlig statisk CPUUsageSnapshot makeCPUUsageSnapshot () {returner nytt CPUUsageSnapshot (System.currentTimeMillis (), getProcessCPUTime ()); } offentlig statisk dobbel getProcessCPUUsage (CPUUsageSnapshot start, CPUUsageSnapshot end) {return ((double) (end.m_CPUTime - start.m_CPUTime)) / (end.m_time - start.m_time); } 

"CPU monitor API" er nesten klar til bruk! Som en siste touch lager jeg en singleton-trådklasse, CPUUsageThread, som automatisk tar øyeblikksbilder av data med jevne mellomrom (0,5 sekunder som standard) og rapporterer dem til et sett med CPU-bruk hendelseslyttere (det kjente observatørmønsteret). De CPUmon class er en demo-lytter som bare skriver ut CPU-bruken til System.out:

 public static void main (String [] args) kaster Unntak {if (args.length == 0) throw new IllegalArgumentException ("use: CPUmon"); CPUUsageThread monitor = CPUUsageThread.getCPUThreadUsageThread (); CPUmon _this = ny CPUmon (); Klasse-app = Class.forName (args [0]); Method appmain = app.getMethod ("main", new Class [] {String []. Class}); String [] appargs = new String [args.length - 1]; System.arraycopy (args, 1, appargs, 0, appargs.length); monitor.addUsageEventListener (_this); monitor.start (); appmain.invoke (null, nytt objekt [] {appargs}); } 

I tillegg CPUmon.main () "pakker" inn en annen Java-hovedklasse med det ene formål å starte CPUUsageThread før du starter den opprinnelige applikasjonen.

Som en demonstrasjon løp jeg CPUmon med SwingSet2 Swing-demo fra JDK 1.3.1 (ikke glem å installere silib.dll til et sted dekket av enten STI OS-miljøvariabel eller java.library.path Java-egenskap):

> java -Djava.library.path =. -cp silib.jar; (min JDK-installasjonsdir) \ demo \ jfc \ SwingSet2 \ SwingSet2.jar CPUmon SwingSet2 [PID: 339] CPU-bruk: 46,8% [PID: 339] CPU-bruk: 51,4% [PID: 339] CPU bruk: 54,8% (under innlasting bruker demoen nesten 100% av en av de to CPUene på maskinen min) ... [PID: 339] CPU-bruk: 46,8% [PID: 339] CPU-bruk: 0% [PID: 339] CPU-bruk: 0% (demo var ferdig med å laste inn alle panelene og er stort sett inaktiv) ... [PID: 339] CPU-bruk: 100% [PID: 339] CPU-bruk: 98,4% [PID: 339] CPU bruk: 97% (jeg byttet til ColorChooserDemo-panelet som kjørte en CPU-intensiv animasjon som brukte begge mine CPUer) ... [PID: 339] CPU-bruk: 81,4% [PID: 339] CPU-bruk: 50% [PID : 339] CPU-bruk: 50% (jeg brukte Windows NT Oppgavebehandling for å justere CPU-affiniteten for "java" -prosessen for å bruke en enkelt CPU) ... 

Selvfølgelig kan jeg se de samme bruksnumrene via oppgavebehandling, men poenget her er at jeg nå har en programmatisk måte å registrere de samme dataene på. Det vil være nyttig for langvarige tester og diagnostisering av serverapplikasjoner. Hele biblioteket (tilgjengelig i ressurser) legger til noen få andre nyttige innfødte metoder, inkludert en for å få prosessen PID (for integrering med eksterne verktøy).

Vladimir Roubtsov har programmert på en rekke språk i mer enn 12 år, inkludert Java siden 1995. For tiden utvikler han bedriftsprogramvare som seniorutvikler for Trilogy i Austin, Texas. Ved koding for moro skyld utvikler Vladimir programvareverktøy basert på Java-byte-kode eller kildekodeinstrumentering.

Lær mer om dette emnet

  • Last ned hele biblioteket som følger med denne artikkelen

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

  • JNI spesifikasjon og opplæringsprogrammer

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

  • For en god oversikt over JNI, se Stuart Dabbs Halloway's Komponentutvikling for Java-plattformen (Addison-Wesley, desember 2001; ISBN0201753065)

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

  • I "Java Tip 92Use the JVM Profiler Interface for Accurate Timing", utforsker Jesper Gortz en alternativ retning for profilering av CPU-bruk. (Imidlertid krever bruk av JVMPI mer arbeid for å beregne CPU-bruk for hele prosessen sammenlignet med denne artikkelens løsning)

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

  • Se Java Q&A indeksside for den fullstendige spørsmål og svar-katalogen

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

  • For mer enn 100 innsiktsfulle Java-tips, besøk JavaWorld 's Java-tips indeksside

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

  • Bla gjennom Core Java seksjon av JavaWorld 's Aktuell indeks

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

  • Få flere av dine spørsmål besvart i vår Java Nybegynner diskusjon

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

  • Melde seg på JavaWorldgratis ukentlige nyhetsbrev via e-post

    //www.javaworld.com/subscribe

  • Du finner et vell av IT-relaterte artikler fra søsterpublikasjonene våre på .net

Denne historien, "Profilering av CPU-bruk fra et Java-program" ble opprinnelig utgitt av JavaWorld.

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