Profil penggunaan CPU dari dalam aplikasi Java

8 November 2002

Q: Bagaimana anda menentukan penggunaan CPU di Java?

J: Jadi, inilah berita baik dan berita buruk. Berita buruk adalah bahawa pertanyaan secara teratur untuk penggunaan CPU tidak mungkin menggunakan Java murni. Tidak ada API untuk ini. Alternatif yang dicadangkan mungkin digunakan Runtime.exec()untuk menentukan ID proses (PID) JVM, memanggil arahan luaran, khusus platform ps, dan menguraikan outputnya untuk PID yang menarik. Tetapi, pendekatan ini rapuh.

Namun, kabar baiknya ialah penyelesaian yang boleh dipercayai dapat dicapai dengan melangkah ke luar Java dan menulis beberapa baris kode C yang berintegrasi dengan aplikasi Java melalui Java Native Interface (JNI). Saya tunjukkan di bawah betapa senangnya dengan membuat perpustakaan JNI ringkas untuk platform Win32. Bahagian Sumber mengandungi pautan ke perpustakaan yang boleh anda sesuaikan untuk keperluan anda sendiri dan port ke platform lain.

Secara umum, JNI agak rumit untuk digunakan. Namun, apabila anda memanggil satu arah sahaja - dari Java ke dalam kod asli - dan berkomunikasi menggunakan jenis data primitif, semuanya tetap sederhana. Terdapat banyak rujukan yang baik (lihat Sumber) di JNI, jadi saya tidak menyediakan tutorial JNI di sini; Saya hanya menggariskan langkah-langkah pelaksanaan saya.

Saya mulakan dengan membuat kelas com.vladium.utils.SystemInformationyang menyatakan kaedah asli, yang mengembalikan bilangan milisaat masa CPU yang digunakan oleh proses semasa sejauh ini:

 getProcessCPUTime lama asli statik awam (); 

Saya menggunakan alat javah dari JDK untuk menghasilkan header C berikut untuk pelaksanaan asli saya yang akan datang:

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

Pada kebanyakan platform Win32, kaedah ini dapat dilaksanakan menggunakan GetProcessTimes()panggilan sistem dan secara harfiah tiga baris kod C:

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

Kaedah ini menambah masa CPU yang dihabiskan untuk melaksanakan kernel dan kod pengguna atas nama proses semasa, menormalkannya dengan jumlah pemproses, dan mengubah hasilnya menjadi milidetik. Yang fileTimeToInt64()adalah fungsi pembantu yang mualaf yang FILETIMEstruktur kepada 64-bit integer, dan s_currentProcessdan s_numberOfProcessorsadalah pembolehubah global yang boleh ditemui dengan mudah dimulakan dalam kaedah JNI yang dinamakan sekali apabila JVM beban perpustakaan asli:

HANDLE statik s_currentProcess; int s_numberOfProcessors statik; JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * reserve) {SYSTEM_INFO systemInfo; s_currentProcess = GetCurrentProcess (); GetSystemInfo (& systemInfo); s_numberOfProcessors = systemInfo.dwNumberOfProcessors; pulangkan JNI_VERSION_1_2; }

Perhatikan bahawa jika anda melaksanakan getProcessCPUTime()pada platform Unix, anda mungkin menggunakan getrusagepanggilan sistem sebagai titik permulaan anda.

Kembali ke Java, memuatkan perpustakaan asli ( silib.dllpada Win32) paling baik dicapai melalui pemula statis di SystemInformationkelas:

rentetan akhir statik persendirian SILIB = "silib"; statik {cuba {System.loadLibrary (SILIB); } tangkapan (UnsatisfiedLinkError e) {System.out.println ("native lib '" + SILIB + "' tidak dijumpai di 'java.library.path':" + System.getProperty ("java.library.path")); baling e; // buang semula}}

Perhatikan bahawa getProcessCPUTime()mengembalikan masa CPU yang digunakan sejak penciptaan proses JVM. Dengan sendirinya, data ini tidak begitu berguna untuk pembuatan profil. Saya memerlukan lebih banyak kaedah Java utiliti untuk merakam tangkapan gambar pada pelbagai masa dan melaporkan penggunaan CPU antara dua titik waktu:

CPUUsageSnapshot kelas akhir statik awam {CPUUsageSnapshot peribadi (lama, panjang CPUT) {m_time = waktu; m_CPUTime = CPUTime; } m_time akhir umum, m_CPUTime; } // akhir CPU statik awam bersarang kelasUsageSnapshot makeCPUUsageSnapshot () {mengembalikan CPUUsageSnapshot baru (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 monitor API" hampir siap digunakan! Sebagai sentuhan terakhir, saya membuat kelas utas tunggal CPUUsageThread, yang secara automatik mengambil gambar data secara berkala (0,5 saat secara lalai) dan melaporkannya kepada satu set pendengar acara penggunaan CPU (corak Pengamat yang sudah biasa). The CPUmonkelas adalah pendengar demo yang hanya mencetak penggunaan CPU untuk System.out:

public static void main (String [] args) membuang Exception {if (args.length == 0) membuang IllegalArgumentException baru ("use: CPUmon"); CPUUsageThread = CPUUsageThread.getCPUThreadUsageThread (); CPUmon _this = CPUmon baru (); Aplikasi kelas = Class.forName (args [0]); Kaedah appmain = app.getMethod ("main", Class baru [] {String []. Class}); String [] appargs = String baru [args.length - 1]; System.arraycopy (args, 1, pakaian, 0, appargs.length); monitor.addUsageEventListener (_this); monitor.start (); appmain.invoke (null, Objek baru [] {appargs}); }

Selain itu, CPUmon.main()"membungkus" kelas utama Java yang lain dengan tujuan bermula CPUUsageThreadsebelum melancarkan aplikasi asal.

Sebagai demonstrasi, saya berlari CPUmondengan demo SwingSet2 Swing dari JDK 1.3.1 (jangan lupa memasang silib.dllke lokasi yang diliputi oleh PATHpemboleh ubah persekitaran OS atau sifat java.library.pathJava):

> java -Djava.library.path =. -cp silib.jar; (pemasangan JDK saya) \ demo \ jfc \ SwingSet2 \ SwingSet2.jar CPUmon SwingSet2 [PID: 339] Penggunaan CPU: 46.8% [PID: 339] Penggunaan CPU: 51.4% [PID: 339] CPU penggunaan: 54.8% (semasa memuatkan, demo menggunakan hampir 100% salah satu daripada dua CPU pada mesin saya) ... [PID: 339] Penggunaan CPU: 46.8% [PID: 339] Penggunaan CPU: 0% [PID: 339] Penggunaan CPU: 0% (demo selesai memuatkan semua panelnya dan kebanyakannya tidak berfungsi) ... [PID: 339] Penggunaan CPU: 100% [PID: 339] Penggunaan CPU: 98.4% [PID: 339] CPU penggunaan: 97% (saya beralih ke panel ColorChooserDemo yang menjalankan animasi intensif CPU yang menggunakan kedua-dua CPU saya) ... [PID: 339] Penggunaan CPU: 81.4% [PID: 339] Penggunaan CPU: 50% [PID : 339] Penggunaan CPU: 50% (Saya menggunakan Windows NT Task Manager untuk menyesuaikan hubungan CPU untuk proses "java" untuk menggunakan CPU tunggal) ...

Sudah tentu, saya dapat melihat nombor penggunaan yang sama melalui pengurus tugas, tetapi intinya adalah bahawa saya sekarang mempunyai cara terprogram untuk merekod data yang sama. Ia akan sangat berguna untuk ujian jangka panjang dan diagnostik aplikasi pelayan. Perpustakaan lengkap (tersedia dalam Sumber) menambah beberapa kaedah asli yang berguna, termasuk satu untuk mendapatkan proses PID (untuk penyatuan dengan alat luaran).

Vladimir Roubtsov telah memprogram dalam berbagai bahasa selama lebih dari 12 tahun, termasuk Java sejak 1995. Pada masa ini, dia mengembangkan perisian perusahaan sebagai pemaju kanan untuk Trilogy di Austin, Texas. Semasa membuat kod untuk bersenang-senang, Vladimir mengembangkan alat perisian berdasarkan kod bait Java atau instrumen kod sumber.

Ketahui lebih lanjut mengenai topik ini

  • Muat turun pustaka lengkap yang menyertai artikel ini

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

  • Spesifikasi dan tutorial JNI

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

  • Untuk gambaran keseluruhan JNI yang baik, lihat Pembangunan Komponen Stuart Dabbs Halloway untuk Platform Java (Addison-Wesley, Disember 2001; ISBN0201753065)

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

  • Dalam "Java Tip 92Gunakan JVM Profiler Interface for Accurate Timing," Jesper Gortz meneroka arah alternatif untuk profil penggunaan CPU. (Namun, menggunakan JVMPI memerlukan lebih banyak pekerjaan untuk menghitung penggunaan CPU untuk keseluruhan proses dibandingkan dengan solusi artikel ini)

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

  • Lihat halaman indeks Soal Jawab Java untuk katalog Q&A lengkap

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

  • Lebih daripada 100 tips Java berwawasan, lawatan JavaWorld ' s Java Tips halaman indeks

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

  • Semak imbas Java Core bahagian JavaWorld ' s Indeks Topikal

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

  • Dapatkan lebih banyak soalan anda dijawab dalam perbincangan Pemula Java kami

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

  • Daftar untuk mendapatkan buletin e-mel percuma JavaWorld

    //www.javaworld.com/subscribe

  • Anda akan mendapat banyak artikel berkaitan IT dari penerbitan saudara kami di .net

Kisah ini, "Profil penggunaan CPU dari dalam aplikasi Java" awalnya diterbitkan oleh JavaWorld.