Programmering

Profilering af CPU-brug fra et Java-program

8. november 2002

Spørgsmål: Hvordan bestemmer du CPU-brug i Java?

EN: Så her er de gode nyheder og de dårlige nyheder. Den dårlige nyhed er, at det umuligt er at bruge ren Java, at programmatisk forespørge om CPU-brug. Der er simpelthen ingen API til dette. Et foreslået alternativ kan bruge Runtime.exec () for at bestemme JVM's proces-id (PID) skal du kalde en ekstern, platformsspecifik kommando som ps, og analyser dens output for PID af interesse. Men denne tilgang er i bedste fald skrøbelig.

Den gode nyhed er dog, at en pålidelig løsning kan opnås ved at træde uden for Java og skrive et par C-kodelinjer, der integreres med Java-applikationen via Java Native Interface (JNI). Jeg viser nedenfor, hvor let det er ved at oprette et simpelt JNI-bibliotek til Win32-platformen. Sektionen Ressourcer indeholder et link til biblioteket, som du kan tilpasse til dine egne behov og port til andre platforme.

Generelt er JNI noget kompleks at bruge. Men når du kun ringer i en retning - fra Java til native-kode - og kommunikerer ved hjælp af primitive datatyper, forbliver tingene enkle. Der er mange gode referencer (se Ressourcer) om JNI, så jeg giver ikke en JNI-tutorial her; Jeg beskriver kun mine implementeringstrin.

Jeg begynder med at oprette en klasse com.vladium.utils.SystemInformation der erklærer en oprindelig metode, der returnerer antallet af millisekunder CPU-tid brugt af den nuværende proces hidtil:

 offentlig statisk native lang getProcessCPUTime (); 

Jeg bruger javah-værktøjet fra JDK til at producere følgende C-header til min fremtidige native implementering:

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

På de fleste Win32-platforme kan denne metode implementeres ved hjælp af GetProcessTimes () systemopkald og er bogstaveligt 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); return (jlong) ((fileTimeToInt64 (& kernelTime) + fileTimeToInt64 (& userTime)) / (s_numberOfProcessors * 10000)); } 

Denne metode tilføjer CPU-tid brugt til at udføre kerne og brugerkode på vegne af den aktuelle proces, normaliserer den med antallet af processorer og konverterer resultatet til millisekunder. Det fileTimeToInt64 () er en hjælperfunktion, der konverterer FILETID struktur til et 64-bit heltal, og s_currentProcess og s_numberOfProcessors er globale variabler, der let kan initialiseres i en JNI-metode, der kaldes en gang, når JVM indlæser det oprindelige bibliotek:

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

Bemærk, at hvis du implementerer getProcessCPUTime () på en Unix-platform, ville du sandsynligvis bruge getrusage systemopkald som dit udgangspunkt.

At komme tilbage til Java, indlæse det oprindelige bibliotek (silib.dll på Win32) opnås bedst via den statiske initialisering i Systeminformation klasse:

 privat statisk endelig String SILIB = "silib"; statisk {prøv {System.loadLibrary (SILIB); } fange (UnsatisfiedLinkError e) {System.out.println ("native lib '" + SILIB + "' ikke fundet i 'java.library.path':" + System.getProperty ("java.library.path")); smide e; // genkast}} 

Noter det getProcessCPUTime () returnerer CPU-tid brugt siden oprettelsen af ​​JVM-processen. I sig selv er disse data ikke særlig nyttige til profilering. Jeg har brug for flere Java-metoder til at registrere snapshots på forskellige tidspunkter og rapportere CPU-brug mellem to tidspunkter:

 offentlig statisk slutklasse CPUUsageSnapshot {privat CPUUsageSnapshot (lang tid, lang CPUTime) {m_time = tid; m_CPUTime = CPUTime; } offentlig endelig lang m_tid, m_CPUTime; } // slutningen af ​​indlejret klasse offentlig statisk CPUUsageSnapshot makeCPUUsageSnapshot () {returner ny CPUUsageSnapshot (System.currentTimeMillis (), getProcessCPUTime ()); } offentlig statisk dobbelt getProcessCPUUsage (CPUUsageSnapshot start, CPUUsageSnapshot end) {return ((double) (end.m_CPUTime - start.m_CPUTime)) / (end.m_time - start.m_time); } 

"CPU monitor API" er næsten klar til brug! Som et sidste touch opretter jeg en singleton-trådklasse, CPUUsageThread, der automatisk tager data-snapshots med regelmæssige intervaller (0,5 sekunder som standard) og rapporterer dem til et sæt CPU-brugshændelseslyttere (det velkendte observatørmønster). Det CPUmon class er en demo-lytter, der blot udskriver CPU-brugen til System.out:

 offentlig statisk ugyldig hoved (String [] args) kaster Undtagelse {hvis (args.length == 0) kaster nyt IllegalArgumentException ("brug: CPUmon"); CPUUsageThread monitor = CPUUsageThread.getCPUThreadUsageThread (); CPUmon _this = ny CPUmon (); Klasse-app = Class.forName (args [0]); Metode appmain = app.getMethod ("main", ny klasse [] {String []. Class}); Streng [] appargs = ny streng [args.længde - 1]; System.arraycopy (args, 1, appargs, 0, appargs.length); monitor.addUsageEventListener (_dette); monitor.start (); appmain.invoke (null, nyt objekt [] {appargs}); } 

Derudover CPUmon.main () "indpakker" en anden Java-hovedklasse med det ene formål at starte CPUUsageThread inden du starter den originale applikation.

Som en demonstration løb jeg CPUmon med SwingSet2 Swing demo fra JDK 1.3.1 (glem ikke at installere silib.dll til et sted, der er dækket af enten STI OS-miljøvariabel eller java.library.path Java-egenskab):

> java -Djava.library.path =. -cp silib.jar; (min JDK-installationsdir) \ demo \ jfc \ SwingSet2 \ SwingSet2.jar CPUmon SwingSet2 [PID: 339] CPU-forbrug: 46,8% [PID: 339] CPU-forbrug: 51,4% [PID: 339] CPU forbrug: 54,8% (under indlæsning bruger demoen næsten 100% af en af ​​de to CPU'er på min maskine) ... [PID: 339] CPU-brug: 46,8% [PID: 339] CPU-brug: 0% [PID: 339] CPU-forbrug: 0% (demo færdig med at indlæse alle dets paneler og er for det meste inaktiv) ... [PID: 339] CPU-brug: 100% [PID: 339] CPU-brug: 98,4% [PID: 339] CPU forbrug: 97% (jeg skiftede til ColorChooserDemo-panelet, som kørte en CPU-intensiv animation, der brugte begge mine CPU'er) ... [PID: 339] CPU-brug: 81,4% [PID: 339] CPU-brug: 50% [PID : 339] CPU-forbrug: 50% (jeg brugte Windows NT Task Manager til at justere CPU-affiniteten til "java" -processen for at bruge en enkelt CPU) ... 

Selvfølgelig kan jeg se de samme brugsnumre via task manager, men pointen her er, at jeg nu har en programmatisk måde at registrere de samme data på. Det vil være nyttigt til langvarige tests og serverapplikationsdiagnostik. Det komplette bibliotek (tilgængeligt i ressourcer) tilføjer et par andre nyttige native metoder, herunder en til at få processen PID (til integration med eksterne værktøjer).

Vladimir Roubtsov har programmeret på en række sprog i mere end 12 år, inklusive Java siden 1995. I øjeblikket udvikler han virksomhedssoftware som seniorudvikler for Trilogy i Austin, Texas. Ved kodning for sjov udvikler Vladimir softwareværktøjer baseret på Java-byte-kode eller kildekodeinstrumentering.

Lær mere om dette emne

  • Download det komplette bibliotek, der ledsager denne artikel

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

  • JNI specifikation og tutorials

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

  • For et godt overblik over JNI, se Stuart Dabbs Halloways Komponentudvikling til Java-platformen (Addison-Wesley, december 2001; ISBN0201753065)

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

  • I "Java Tip 92Use JVM Profiler Interface for Accurate Timing" udforsker Jesper Gortz en alternativ retning til profilering af CPU-brug. (Brug af JVMPI kræver dog mere arbejde for at beregne CPU-brug for hele processen sammenlignet med denne artikels løsning)

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

  • Se Java Q&A indeksside for det fulde Q & A-katalog

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

  • For mere end 100 indsigtsfulde Java-tip, besøg JavaWorld 's Java-tip indeksside

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

  • Gennemse Core Java sektion af JavaWorld 's Aktuelt indeks

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

  • Få flere af dine spørgsmål besvaret i vores Java Nybegynder diskussion

    //forums.devworld.com/[email protected]@.ee6b804

  • Tilmeld dig JavaWorld's gratis ugentlige nyhedsbreve via e - mail

    //www.javaworld.com/subscribe

  • Du finder et væld af it-relaterede artikler fra vores søsterpublikationer på .net

Denne historie, "Profilering af CPU-brug fra en Java-applikation" blev oprindeligt udgivet af JavaWorld.