Ohjelmointi

Suorittimen käytön profilointi Java-sovelluksessa

8. marraskuuta 2002

K: Kuinka määrität suorittimen käytön Javassa?

A: Joten tässä on hyvät ja huonot uutiset. Huono uutinen on, että suorittimen käytön ohjelmallinen kysely on mahdotonta puhdasta Java-ohjelmaa käyttämällä. Tätä varten ei yksinkertaisesti ole API: ta. Ehdotettu vaihtoehto saattaa käyttää Runtime.exec () määrittääksesi JVM: n prosessitunnuksen (PID), kutsu ulkoinen, alustakohtainen komento, kuten psja jäsennä sen tuotos kiinnostavan PID: n mukaan. Mutta tämä lähestymistapa on parhaimmillaan hauras.

Hyvä uutinen on kuitenkin se, että luotettava ratkaisu voidaan saavuttaa siirtymällä Javan ulkopuolelle ja kirjoittamalla muutama C-koodirivi, joka integroituu Java-sovellukseen Java Native Interface (JNI) -liittymän kautta. Näytän alla, kuinka helppoa se on luomalla yksinkertainen JNI-kirjasto Win32-alustalle. Resurssit-osiossa on linkki kirjastoon, jonka voit mukauttaa omiin tarpeisiisi, ja portti muille alustoille.

Yleensä JNI on jonkin verran monimutkainen käyttää. Kuitenkin, kun soitat vain yhteen suuntaan - Javasta alkuperäiseen koodiin - ja kommunikoi primitiivisten tietotyyppien avulla, asiat pysyvät yksinkertaisina. JNI: ssä on monia hyviä viitteitä (katso Resurssit), joten en tarjoa JNI-opetusohjelmaa täällä; Esittelen vain toteutusvaiheeni.

Aloitan luomalla luokan com.vladium.utils.SystemInformation joka ilmoittaa natiivimenetelmän, joka palauttaa nykyisen prosessin tähän mennessä käyttämän millisekuntien suorittimen ajan:

 julkinen staattinen natiivi pitkä getProcessCPUTime (); 

Käytän JDK: n javah-työkalua tuottamaan seuraavan C-otsikon tulevaa alkuperäistä toteutusta varten:

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

Useimmissa Win32-alustoissa tämä menetelmä voidaan toteuttaa käyttämällä GetProcessTimes () järjestelmäkutsu ja on kirjaimellisesti kolme riviä C-koodia:

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_numeroOfProcessors * 10000)); } 

Tämä menetelmä lisää CPU-ajan, joka kului ytimen ja käyttäjäkoodin suorittamiseen nykyisen prosessin puolesta, normalisoi sen prosessorien lukumäärällä ja muuntaa tuloksen millisekunteiksi. fileTimeToInt64 () on auttajafunktio, joka muuntaa FILETIME rakenne 64-bittiseksi kokonaisluvuksi ja s_currentProcess ja s_numberOfProcessors ovat globaaleja muuttujia, jotka voidaan alustaa kätevästi JNI-menetelmällä, jota kutsutaan kerran, kun JVM lataa natiivikirjaston:

staattinen HANDLE s_currentProcess; staattinen int s_numberOfProcessors; JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * varattu) {SYSTEM_INFO systemInfo; s_currentProcess = GetCurrentProcess (); GetSystemInfo (& systemInfo); s_numeroOfProcessors = systemInfo.dwNumberOfProcessors; palaa JNI_VERSION_1_2; } 

Huomaa, että jos toteutat getProcessCPUTime () Unix-alustalla todennäköisesti käyttäisit repiminen järjestelmäpuhelu lähtökohtana.

Paluu Java-sovellukseen, natiivikirjaston lataaminen (silib.dll Win32: ssä) saavutetaan parhaiten Windowsin staattisella alustusohjelmalla Järjestelmän tiedot luokka:

 yksityinen staattinen lopullinen merkkijono SILIB = "silib"; staattinen {kokeile {System.loadLibrary (SILIB); } catch (UnsatisfiedLinkError e) {System.out.println ("native lib" "+ SILIB +" "ei löydy kansiosta 'java.library.path':" + System.getProperty ("java.library.path")); heittää e; // heittää uudelleen}} 

Ota huomioon, että getProcessCPUTime () palauttaa JVM-prosessin luomisesta kuluneen suorittimen ajan. Nämä tiedot eivät sinänsä ole erityisen hyödyllisiä profiloinnissa. Tarvitsen enemmän Java-apuvälineitä datan tilannekuvien tallentamiseksi eri aikoina ja suorittimen käytön raportoimiseksi kahden ajankohdan välillä:

 julkinen staattinen lopullinen luokka CPUUsageSnapshot {private CPUUsageSnapshot (pitkä aika, pitkä CPUTime) {m_time = aika; m_CPUTime = CPUTime; } julkinen lopullinen pitkä m_time, m_CPUTime; } // sisäkkäisen luokan loppu julkinen staattinen CPUUsageSnapshot makeCPUUsageSnapshot () {palauta uusi CPUUsageSnapshot (System.currentTimeMillis (), getProcessCPUTime ()); } public staattinen kaksinkertainen getProcessCPUUsage (CPUUsageSnapshot start, CPUUsageSnapshot end) {return ((double) (end.m_CPUTime - start.m_CPUTime)) / (end.m_time - start.m_time); } 

"CPU monitor API" on melkein käyttövalmis! Viimeisenä kosketuksena luon yksisäikeisen luokan, CPUUsageThread, joka ottaa datan tilannekuvat automaattisesti säännöllisin väliajoin (oletusarvoisesti 0,5 sekuntia) ja raportoi ne joukolle suorittimen käyttötapahtumien kuuntelijoita (tuttu tarkkailijan malli). CPUmon class on demokuuntelija, joka vain tulostaa suorittimen käytön System.out:

 public static void main (String [] args) heittää poikkeuksen {if (args.length == 0) heittää uuden IllegalArgumentException ("käyttö: CPUmon"); CPUUsageThread monitor = CPUUsageThread.getCPUThreadUsageThread (); CPUmon _this = uusi CPUmon (); Class-sovellus = Class.forName (argumentit [0]); Menetelmä appmain = app.getMethod ("main", uusi luokka [] {String []. Class}); Merkkijono [] appargs = uusi merkkijono [arg.pituus - 1]; System.arraycopy (args, 1, appargs, 0, appargs.length); monitor.addUsageEventListener (_tämä); monitor.start (); appmain.invoke (null, uusi objekti [] {appargs}); } 

Lisäksi, CPUmon.main () "kääri" toisen Java-pääluokan, jonka ainoa tarkoitus on aloittaa CPUUsageThread ennen alkuperäisen sovelluksen käynnistämistä.

Mielenosoituksena juoksin CPUmon JDK 1.3.1: n SwingSet2 Swing-demon kanssa (älä unohda asentaa silib.dll sijaintiin, jonka joko PATH Käyttöjärjestelmän ympäristömuuttuja tai java.library.path Java-ominaisuus):

> java -Djava.library.path =. -cp silib.jar; (minun JDK-asennusohjeeni) \ demo \ jfc \ SwingSet2 \ SwingSet2.jar CPUmon SwingSet2 [PID: 339] Suorittimen käyttö: 46,8% [PID: 339] Suorittimen käyttö: 51,4% [PID: 339] CPU käyttö: 54,8% (latauksen aikana demo käyttää lähes 100% koneeni kahdesta suorittimesta) ... [PID: 339] Suorittimen käyttö: 46,8% [PID: 339] Suorittimen käyttö: 0% [PID: 339] CPU: n käyttö: 0% (demo on ladannut kaikki paneelit ja on enimmäkseen tyhjäkäynnillä) ... [PID: 339] CPU: n käyttö: 100% [PID: 339] CPU: n käyttö: 98.4% [PID: 339] CPU käyttö: 97% (vaihdoin ColorChooserDemo-paneeliin, jossa suoritettiin prosessoreita vaativa animaatio, joka käytti molempia keskusyksikköjäni) ... [PID: 339] Suorittimen käyttö: 81,4% [PID: 339] Suorittimen käyttö: 50% [PID : 339] Suorittimen käyttö: 50% (käytin Windows NT Tehtävienhallintaa säätääksesi "java" -prosessorin affiniteettia yhden prosessorin käyttämiseen) ... 

Tietysti voin katsella samoja käyttönumeroita tehtävänhallinnan kautta, mutta tässä on se, että minulla on nyt ohjelmallinen tapa tallentaa samat tiedot. Se on hyödyllinen pitkäkestoisissa testeissä ja palvelinsovellusten diagnostiikassa. Koko kirjasto (saatavana Resursseista) lisää muutamia muita hyödyllisiä natiivimenetelmiä, mukaan lukien yhden prosessin PID: n saamiseksi (integroimiseksi ulkoisten työkalujen kanssa).

Vladimir Roubtsov on ohjelmoinut eri kielillä yli 12 vuoden ajan, mukaan lukien Java vuodesta 1995. Tällä hetkellä hän kehittää yritysohjelmistoja Trilogy-palvelun vanhempana kehittäjänä Austinissa, Texasissa. Kun hauskaa koodataan, Vladimir kehittää ohjelmistotyökaluja, jotka perustuvat Java-tavukoodiin tai lähdekoodiin.

Lisätietoja tästä aiheesta

  • Lataa koko tämän artikkelin mukana oleva kirjasto

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

  • JNI-määritykset ja oppaat

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

  • Stuart Dabbs Halloway's tarjoaa hyvän yleiskatsauksen JNI: stä Komponenttien kehittäminen Java-alustalle (Addison-Wesley, joulukuu 2001; ISBN0201753065)

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

  • Jesper Gortz etsii kohdassa "Java Tip 92Use the JVM Profiler Interface for Precision Timing" tarkempaa vaihtoehtoa prosessorin profiloinnille. (JVMPI: n käyttö vaatii kuitenkin enemmän työtä suorittimen käytön laskemiseksi koko prosessille verrattuna tämän artikkelin ratkaisuun.)

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

  • Katso Java-kysymykset ja vastaukset hakemistosivu täydelliseen Q & A-luetteloon

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

  • Saat yli 100 oivaltavaa Java-vinkkiä osoitteesta JavaWorld 's Java-vinkkejä hakemistosivu

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

  • Selaa Java-ydin osa JavaWorld 's Ajankohtainen hakemisto

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

  • Saat enemmän vastauksia kysymyksiisi Java-aloittelija keskustelu

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

  • Ilmottautua JavaWorldilmaiset viikoittaiset sähköpostiuutiskirjeet

    //www.javaworld.com/subscribe

  • Löydät runsaasti tietotekniikkaan liittyviä artikkeleita sisarjulkaisuistamme .net

Tämän tarinan "Suorittimen käytön profilointi Java-sovelluksen sisällä" julkaisi alun perin JavaWorld.

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