Programování

Profilování využití procesoru z aplikace Java

8. listopadu 2002

Otázka: Jak zjistíte využití CPU v Javě?

A: Tady jsou dobré i špatné zprávy. Špatnou zprávou je, že programové dotazování na využití CPU není možné pomocí čisté Javy. Pro toto prostě neexistuje API. Může být použita navrhovaná alternativa Runtime.exec () Chcete-li zjistit ID procesu (PID) JVM, zavolejte externí příkaz specifický pro platformu psa analyzovat jeho výstup pro PID zájmu. Ale tento přístup je přinejlepším křehký.

Dobrou zprávou však je, že spolehlivého řešení lze dosáhnout krokem mimo prostředí Java a napsáním několika řádků kódu C, které se integrují s aplikací Java prostřednictvím nativního rozhraní Java (JNI). Níže ukazuji, jak je to snadné, vytvořením jednoduché knihovny JNI pro platformu Win32. Sekce Zdroje obsahuje odkaz na knihovnu, kterou si můžete přizpůsobit pro své vlastní potřeby a portovat na jiné platformy.

Obecně je použití JNI poněkud složité. Když však voláte pouze jedním směrem - z Javy do nativního kódu - a komunikujete pomocí primitivních datových typů, věci zůstávají jednoduché. Na JNI existuje mnoho dobrých odkazů (viz Zdroje), takže zde neposkytuji výukový program JNI; Jen načrtnu své implementační kroky.

Začínám vytvořením třídy com.vladium.utils.SystemInformation který deklaruje nativní metodu, která vrací počet milisekund času CPU, který dosud použil aktuální proces:

 public static native long getProcessCPUTime (); 

Používám nástroj javah z JDK k vytvoření následující hlavičky C pro svou budoucí nativní implementaci:

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

Na většině platforem Win32 lze tuto metodu implementovat pomocí GetProcessTimes () systémové volání a jsou to doslova tři řádky kódu C:

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); návrat (jlong) ((fileTimeToInt64 (& kernelTime) + fileTimeToInt64 (& userTime)) / (s_numberOfProcessors * 10 000)); } 

Tato metoda přidává čas CPU strávený prováděním jádra a uživatelského kódu jménem aktuálního procesu, normalizuje jej podle počtu procesorů a převádí výsledek na milisekundy. The fileTimeToInt64 () je pomocná funkce, která převádí FILETIME struktura na 64bitové celé číslo a s_currentProcess a s_numberOfProcessors jsou globální proměnné, které lze pohodlně inicializovat v metodě JNI, která se volá jednou, když JVM načte nativní knihovnu:

static HANDLE s_currentProcess; static int s_numberOfProcessors; JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * rezervováno) {SYSTEM_INFO systemInfo; s_currentProcess = GetCurrentProcess (); GetSystemInfo (& systemInfo); s_numberOfProcessors = systemInfo.dwNumberOfProcessors; vrátit JNI_VERSION_1_2; } 

Všimněte si, že pokud implementujete getProcessCPUTime () na unixové platformě byste pravděpodobně použili getrusage systémový hovor jako výchozí bod.

Vrácení se k Javě, načítání nativní knihovny (silib.dll na Win32) se nejlépe provádí pomocí statického inicializátoru v Systémové informace třída:

 soukromý statický konečný řetězec SILIB = "silib"; static {try {System.loadLibrary (SILIB); } catch (UnsatisfiedLinkError e) {System.out.println ("native lib '" + SILIB + "' nebyl nalezen v 'java.library.path':" + System.getProperty ("java.library.path")); hod e; // re-throw}} 

Všimněte si, že getProcessCPUTime () vrací čas CPU použitý od vytvoření procesu JVM. Tato data sama o sobě nejsou pro profilování zvlášť užitečná. Potřebuji více užitečných metod Java pro záznam datových snímků v různých časech a hlášení využití procesoru mezi libovolnými dvěma časovými body:

 veřejná statická konečná třída CPUUsageSnapshot {soukromá CPUUsageSnapshot (dlouhá doba, dlouhá CPUTime) {m_time = čas; m_CPUTime = CPUTime; } veřejné konečné dlouhé m_time, m_CPUTime; } // konec vnořené třídy public static CPUUsageSnapshot makeCPUUsageSnapshot () {vrátit nový 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 monitorovací API“ je téměř připraveno k použití! Jako poslední dotek vytvořím třídu singletonových vláken, CPUUsageThread, který automaticky pořizuje datové snímky v pravidelných intervalech (ve výchozím nastavení 0,5 sekundy) a hlásí je sadě posluchačů událostí využití CPU (známý vzor Observer). The CPUmon class je ukázkový posluchač, který jednoduše vytiskne využití CPU System.out:

 public static void main (String [] args) vyvolá výjimku {if (args.length == 0) vyvolá novou IllegalArgumentException ("usage: CPUmon"); CPUUsageThread monitor = CPUUsageThread.getCPUThreadUsageThread (); CPUmon _this = nový CPUmon (); Class app = Class.forName (args [0]); Metoda appmain = app.getMethod ("main", nová třída [] {řetězec []. Třída}); Řetězec [] zařízení = nový Řetězec [args.length - 1]; System.arraycopy (arg, 1, aparáty, 0, aparatura.délka); monitor.addUsageEventListener (_this); monitor.start (); appmain.invoke (null, nový objekt [] {přístroje}); } 

Dodatečně, CPUmon.main () "zabalí" další hlavní třídu Java s jediným účelem spuštění CPUUsageThread před spuštěním původní aplikace.

Jako ukázku jsem běžel CPUmon s ukázkou SwingSet2 Swing od JDK 1.3.1 (nezapomeňte nainstalovat silib.dll na místo pokryté buď CESTA Proměnná prostředí OS nebo cesta java.library.path Vlastnost Java):

> java -Djava.library.path =. -cp silib.jar; (můj instalační adresář JDK) \ demo \ jfc \ SwingSet2 \ SwingSet2.jar CPUmon SwingSet2 [PID: 339] využití CPU: 46,8% [PID: 339] využití CPU: 51,4% [PID: 339] CPU využití: 54,8% (při načítání demo využívá téměř 100% jednoho ze dvou procesorů na mém stroji) ... [PID: 339] využití CPU: 46,8% [PID: 339] využití CPU: 0% [PID: 339] Využití CPU: 0% (ukázka dokončila načítání všech svých panelů a je většinou nečinná) ... [PID: 339] Využití CPU: 100% [PID: 339] Využití CPU: 98,4% [PID: 339] CPU využití: 97% (přepnul jsem na panel ColorChooserDemo, který spustil animaci náročnou na CPU, která používala oba mé CPU) ... [PID: 339] využití CPU: 81,4% [PID: 339] využití CPU: 50% [PID : 339] Využití CPU: 50% (pomocí Správce úloh Windows NT jsem upravil afinitu CPU pro proces „java“ tak, aby používal jeden CPU) ... 

Samozřejmě můžu sledovat stejná čísla využití prostřednictvím správce úloh, ale zde jde o to, že nyní mám programový způsob záznamu stejných dat. Hodí se pro dlouhodobé testy a diagnostiku serverových aplikací. Kompletní knihovna (k dispozici ve zdrojích) přidává několik dalších užitečných nativních metod, včetně jedné pro získání PID procesu (pro integraci s externími nástroji).

Vladimir Roubtsov programuje v různých jazycích více než 12 let, včetně Javy od roku 1995. V současné době vyvíjí podnikový software jako senior vývojář pro Trilogy v texaském Austinu. Při kódování pro zábavu Vladimir vyvíjí softwarové nástroje založené na bajtovém kódu Java nebo instrumentaci zdrojového kódu.

Další informace o tomto tématu

  • Stáhněte si kompletní knihovnu, která je přiložena k tomuto článku

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

  • Specifikace JNI a výukové programy

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

  • Dobrý přehled o JNI najdete v Stuart Dabbs Halloway's Vývoj komponent pro platformu Java (Addison-Wesley, prosinec 2001; ISBN0201753065)

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

  • V „Java Tip 92Use the JVM Profiler Interface for Accurate Timing,“ Jesper Gortz zkoumá alternativní směr pro profilování využití CPU. (Použití JVMPI však vyžaduje více práce s výpočtem využití procesoru pro celý proces ve srovnání s řešením tohoto článku)

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

  • Viz Java Q&A indexová stránka pro úplný katalog otázek a odpovědí

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

  • Více než 100 užitečných tipů pro Java naleznete na JavaWorld 's Tipy pro Java indexová stránka

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

  • Procházejte Core Java část JavaWorld 's Aktuální rejstřík

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

  • Získejte více odpovědí na vaše otázky v našem Java začátečník diskuse

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

  • Přihlásit se JavaWorldbezplatné týdenní e-mailové zpravodaje

    //www.javaworld.com/subscribe

  • Spoustu článků o IT z našich sesterských publikací najdete na .net

Tento příběh, „Profilování využití CPU v rámci aplikace Java“, původně publikoval JavaWorld.