Profiliranje upotrebe CPU-a unutar Java aplikacije

8. studenog 2002

P: Kako utvrđujete upotrebu procesora u Javi?

O: Dakle, evo dobrih i loših vijesti. Loša vijest je da je programsko postavljanje upita za upotrebu procesora nemoguće pomoću čiste Jave. Za to jednostavno ne postoji API. Predložena alternativa može se koristiti Runtime.exec()za određivanje ID-a procesa JVM-a (PID), pozivanje vanjske naredbe specifične za platformu psi analiziranje njezina rezultata za PID od interesa. Ali, ovaj je pristup u najboljem slučaju krhak.

Dobra vijest je međutim da se pouzdano rješenje može postići izlaskom izvan Jave i pisanjem nekoliko redaka C koda koji se integriraju s aplikacijom Java putem Java Native Interface (JNI). Ispod sam pokazao koliko je to jednostavno stvaranjem jednostavne JNI biblioteke za Win32 platformu. Odjeljak Resursi sadrži vezu do knjižnice koju možete prilagoditi za svoje potrebe i priključiti na druge platforme.

Općenito, JNI je donekle složen za upotrebu. Međutim, kada zovete samo u jednom smjeru - s Jave na izvorni kôd - i komunicirate primitivnim vrstama podataka, stvari ostaju jednostavne. Postoji mnogo dobrih referenci (vidi Resurse) o JNI, pa ovdje ne pružam JNI tutorial; Ja samo opisujem svoje korake implementacije.

Počinjem stvaranjem klase com.vladium.utils.SystemInformationkoja deklarira nativnu metodu, koja vraća broj milisekundi CPU vremena koje je dosadašnji proces koristio:

 javni statični izvorni long getProcessCPUTime (); 

Koristim javah alat iz JDK za izradu sljedećeg C zaglavlja za moju buduću izvornu implementaciju:

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

Na većini platformi Win32, ova se metoda može implementirati pomoću GetProcessTimes()sistemskog poziva i doslovno je u tri retka C koda:

JNIEXPORT jlong ​​JNICALL Java_com_vladium_utils_SystemInformation_getProcessCPUTime (JNIEnv * env, jclass cls) {FILETIME createTime, exitTime, kernelTime, userTime; GetProcessTimes (s_currentProcess, & createTime, & exitTime, & kernelTime, & userTime); return (jlong) ((fileTimeToInt64 (& kernelTime) + fileTimeToInt64 (& userTime)) / (s_numberOfProcessors * 10000)); }

Ova metoda dodaje CPU vrijeme provedeno u izvršavanju jezgre i korisničkog koda u ime trenutnog procesa, normalizira ga prema broju procesora i pretvara rezultat u milisekunde. Funkcija fileTimeToInt64()je pomoćna koja FILETIMEstrukturu pretvara u 64-bitni cijeli broj s_currentProcessi s_numberOfProcessorsjesu globalne varijable koje se mogu prikladno inicijalizirati u JNI metodi koja se jednom poziva kada JVM učita matičnu knjižnicu:

statički HANDLE s_currentProcess; statički int s_numberOfProcessors; JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * rezervirano) {SYSTEM_INFO systemInfo; s_currentProcess = GetCurrentProcess (); GetSystemInfo (& systemInfo); s_numberOfProcessors = systemInfo.dwNumberOfProcessors; povratak JNI_VERSION_1_2; }

Imajte na umu da ako implementirate getProcessCPUTime()na Unix platformu, vjerojatno biste getrusagekao početnu točku koristili sistemski poziv.

Povratak na Javu, učitavanje matične biblioteke ( silib.dllna Win32) najbolje je postići statičkim inicijalizatorom u SystemInformationklasi:

privatni statički završni niz SILIB = "silib"; static {try {System.loadLibrary (SILIB); } catch (UnsatisfiedLinkError e) {System.out.println ("native lib '" + SILIB + "' nije pronađen u 'java.library.path':" + System.getProperty ("java.library.path")); baciti e; // ponovno bacanje}}

Imajte na umu da getProcessCPUTime()vraća procesorsko vrijeme korišteno od stvaranja JVM procesa. Sami po sebi, ovi podaci nisu osobito korisni za profiliranje. Trebam više korisnih Java metoda za snimanje snimaka podataka u različito vrijeme i izvještavanje o korištenju procesora između bilo koje dvije vremenske točke:

javna statička završna klasa CPUUsageSnapshot {private CPUUsageSnapshot (long time, long CPUTime) {m_time = time; m_CPUTime = CPUTime; } javno konačno dugo m_time, m_CPUTime; } // kraj ugniježđene klase javni statički CPUUsageSnapshot makeCPUUsageSnapshot () {vratiti novi CPUUsageSnapshot (System.currentTimeMillis (), getProcessCPUTime ()); } javni statički dvostruki getProcessCPUUsage (CPUUsageSnapshot start, CPUUsageSnapshot end) {return ((double) (end.m_CPUTime - start.m_CPUTime)) / (end.m_time - start.m_time); }

"API CPU monitora" gotovo je spreman za upotrebu! Kao posljednji dodir, kreiram klasu singleton niti CPUUsageThreadkoja automatski snima snimke podataka u redovitim intervalima (prema zadanim postavkama 0,5 sekunde) i prijavljuje ih skupu slušatelja događaja upotrebe CPU-a (poznati obrazac Observer). Predavanje CPUmonje demo slušatelj koji jednostavno ispisuje upotrebu procesora na System.out:

public static void main (String [] args) baca iznimku {if (args.length == 0) baciti novi IllegalArgumentException ("upotreba: CPUmon"); CPUUsageThread monitor = CPUUsageThread.getCPUThreadUsageThread (); CPUmon _this = novi CPUmon (); Aplikacija klase = Class.forName (args [0]); Metoda appmain = app.getMethod ("main", nova klasa [] {String []. Class}); String [] appargs = novi String [args.length - 1]; System.arraycopy (args, 1, appargs, 0, appargs.length); monitor.addUsageEventListener (_this); monitor.start (); appmain.invoke (null, novi objekt [] {appargs}); }

Uz to, CPUmon.main()"omotava" drugu glavnu klasu Jave s jedinom svrhom pokretanja CPUUsageThreadprije pokretanja izvornog programa.

Kao demonstraciju trčao sam CPUmons demonstracijom SwingSet2 Swing iz JDK 1.3.1 (ne zaboravite instalirati silib.dllna mjesto pokriveno PATHvarijablom okruženja OS-a ili java.library.pathsvojstvom Java):

> java -Djava.library.path =. -cp silib.jar; (moj direktorij za instalaciju JDK-a) \ demo \ jfc \ SwingSet2 \ SwingSet2.jar CPUmon SwingSet2 [PID: 339] Upotreba CPU-a: 46,8% [PID: 339] Upotreba CPU-a: 51,4% [PID: 339] CPU upotreba: 54,8% (dok se učitava, demonstracija koristi gotovo 100% jednog od dva CPU-a na mom stroju) ... [PID: 339] Upotreba CPU-a: 46,8% [PID: 339] Upotreba CPU-a: 0% [PID: 339] Upotreba CPU-a: 0% (demonstracija je završila s učitavanjem svih ploča i uglavnom je u praznom hodu) ... [PID: 339] Upotreba CPU-a: 100% [PID: 339] Upotreba CPU-a: 98,4% [PID: 339] CPU upotreba: 97% (prebacio sam se na ColorChooserDemo ploču koja je pokrenula CPU-intenzivnu animaciju koja je koristila oba moja CPU-a) ... [PID: 339] Korištenje CPU-a: 81,4% [PID: 339] Korištenje CPU-a: 50% [PID : 339] Korištenje CPU-a: 50% (Koristio sam Windows NT Task Manager da bih prilagodio afinitet CPU-a za "java" proces da bih koristio jedan CPU) ...

Naravno, mogu gledati iste brojeve korištenja putem upravitelja zadataka, ali poanta je ovdje da sada imam programski način za snimanje istih podataka. Dobro će doći za dugotrajne testove i dijagnostiku aplikacijskih poslužitelja. Kompletna knjižnica (dostupna u Resursima) dodaje još nekoliko korisnih izvornih metoda, uključujući onu za dobivanje procesnog PID-a (za integraciju s vanjskim alatima).

Vladimir Roubtsov programira na raznim jezicima više od 12 godina, uključujući Javu od 1995. Trenutno razvija korporativni softver kao stariji programer za Trilogy u Austinu u Teksasu. Kada kodira iz zabave, Vladimir razvija softverske alate temeljene na Java bajt kodu ili instrumentaciji izvornog koda.

Saznajte više o ovoj temi

  • Preuzmite kompletnu biblioteku koja prati ovaj članak

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

  • JNI specifikacija i vodiči

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

  • Za dobar pregled JNI, pogledajte Stuart Dabbs Halloway's Razvoj komponenata za Java platformu (Addison-Wesley, prosinac 2001.; ISBN0201753065)

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

  • U "Java Savjet 92Koristite sučelje JVM Profiler za precizno određivanje vremena", Jesper Gortz istražuje alternativni smjer za profiliranje upotrebe CPU-a. (Međutim, upotreba JVMPI zahtijeva više posla za izračunavanje upotrebe CPU-a za cijeli proces u usporedbi s rješenjem ovog članka)

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

  • Potpuni katalog Pitanja potražite na stranici s pitanjima i odgovorima Java

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

  • Za više od 100 uvida Java savjete, posjet JavaWorld” s Java Savjet indeks stranica

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

  • Pregledaj core Java dio JavaWorld” s Aktuelno Indeks

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

  • Nađite više odgovora na svoja pitanja u našoj raspravi za Java početnike

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

  • Prijavite se za besplatne tjedne biltene e-pošte JavaWorld

    //www.javaworld.com/subscribe

  • Mnoštvo članaka vezanih uz IT iz naših sestrinskih publikacija pronaći ćete na .net

Ovu priču, "Profiliranje upotrebe CPU-a iz Java aplikacije" izvorno je objavio JavaWorld.