Membuat profil penggunaan CPU dari dalam aplikasi Java

8 November 2002

T: Bagaimana Anda menentukan penggunaan CPU di Java?

J: Jadi, inilah kabar baik dan kabar buruknya. Kabar buruknya adalah permintaan secara terprogram untuk penggunaan CPU tidak mungkin dilakukan menggunakan Java murni. Tidak ada API untuk ini. Alternatif yang disarankan mungkin digunakan Runtime.exec()untuk menentukan ID proses (PID) JVM, memanggil perintah eksternal khusus platform seperti ps, dan mengurai outputnya untuk PID yang diinginkan. Tapi, pendekatan ini paling rapuh.

Kabar baiknya, bagaimanapun, adalah bahwa solusi yang dapat diandalkan dapat dicapai dengan keluar dari Java dan menulis beberapa baris kode C yang terintegrasi dengan aplikasi Java melalui Java Native Interface (JNI). Saya tunjukkan di bawah betapa mudahnya dengan membuat perpustakaan JNI sederhana untuk platform Win32. Bagian Resources berisi link ke library yang dapat Anda sesuaikan untuk kebutuhan Anda sendiri dan port ke platform lain.

Secara umum, JNI agak rumit untuk digunakan. Namun, jika Anda hanya memanggil satu arah — dari Java ke kode native — dan berkomunikasi menggunakan tipe data primitif, semuanya tetap sederhana. Ada banyak referensi bagus (lihat Sumberdaya) tentang JNI, jadi saya tidak menyediakan tutorial JNI di sini; Saya hanya menguraikan langkah-langkah implementasi saya.

Saya mulai dengan membuat kelas com.vladium.utils.SystemInformationyang mendeklarasikan metode native, yang mengembalikan jumlah milidetik waktu CPU yang digunakan oleh proses saat ini sejauh ini:

 publik getProcessCPUTime panjang asli statis (); 

Saya menggunakan alat javah dari JDK untuk menghasilkan header C berikut untuk implementasi asli saya di masa mendatang:

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

Pada sebagian besar platform Win32, metode ini dapat diterapkan menggunakan GetProcessTimes()panggilan sistem dan secara harfiah terdiri dari tiga baris kode 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); return (jlong) ((fileTimeToInt64 (& kernelTime) + fileTimeToInt64 (& userTime)) / (s_numberOfProcessors * 10000)); }

Metode ini menambahkan waktu CPU yang dihabiskan untuk mengeksekusi kernel dan kode pengguna atas nama proses saat ini, menormalkannya dengan jumlah prosesor, dan mengubah hasilnya menjadi milidetik. The fileTimeToInt64()adalah fungsi pembantu yang bertobat tersebut FILETIMEstruktur integer 64-bit, dan s_currentProcessdan s_numberOfProcessorsadalah variabel global yang dapat dengan mudah diinisialisasi dalam metode JNI yang dipanggil sekali ketika JVM beban perpustakaan asli:

statis HANDLE s_currentProcess; static int s_numberOfProcessors; JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * reserved) {SYSTEM_INFO systemInfo; s_currentProcess = GetCurrentProcess (); GetSystemInfo (& systemInfo); s_numberOfProcessors = systemInfo.dwNumberOfProcessors; mengembalikan JNI_VERSION_1_2; }

Perhatikan bahwa jika Anda menerapkan getProcessCPUTime()pada platform Unix, Anda kemungkinan akan menggunakan getrusagepanggilan sistem sebagai titik awal Anda.

Kembali ke Java, memuat pustaka asli ( silib.dlldi Win32) paling baik dilakukan melalui penginisialisasi statis di SystemInformationkelas:

private static final String SILIB = "silib"; statis {coba {System.loadLibrary (SILIB); } catch (UnsatisfiedLinkError e) {System.out.println ("native lib '" + SILIB + "' tidak ditemukan di 'java.library.path':" + System.getProperty ("java.library.path")); melempar e; // lempar ulang}}

Perhatikan bahwa getProcessCPUTime()mengembalikan waktu CPU yang digunakan sejak pembuatan proses JVM. Dengan sendirinya, data ini tidak terlalu berguna untuk pembuatan profil. Saya memerlukan lebih banyak metode Java utilitas untuk merekam snapshot data pada berbagai waktu dan melaporkan penggunaan CPU antara dua titik waktu:

public static final class CPUUsageSnapshot {private CPUUsageSnapshot (lama, CPUTime lama) {m_time = time; m_CPUTime = CPUTime; } m_time panjang publik terakhir, m_CPUTime; } // akhir kelas bertingkat public static CPUUsageSnapshot makeCPUUsageSnapshot () {return new CPUUsageSnapshot (System.currentTimeMillis (), getProcessCPUTime ()); } getProcessCPUUsage ganda statis publik (mulai CPUUsageSnapshot, 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 otomatis mengambil snapshot data secara berkala (0,5 detik secara default) dan melaporkannya ke satu set pendengar peristiwa penggunaan CPU (pola Observer yang sudah dikenal). The CPUmonkelas pendengar demo yang hanya mencetak penggunaan CPU untuk System.out:

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

Selain itu, CPUmon.main()"membungkus" kelas utama Java lainnya dengan satu-satunya tujuan memulai CPUUsageThreadsebelum meluncurkan aplikasi asli.

Sebagai demonstrasi, saya menjalankan CPUmondemo SwingSet2 Swing dari JDK 1.3.1 (jangan lupa untuk menginstal silib.dllke lokasi yang dicakup oleh PATHvariabel lingkungan OS atau java.library.pathproperti Java):

> java -Djava.library.path =. -cp silib.jar; (direktori install 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% (saat memuat, demo menggunakan hampir 100% dari salah satu dari dua CPU di mesin saya) ... [PID: 339] Penggunaan CPU: 46,8% [PID: 339] Penggunaan CPU: 0% [PID: 339] Penggunaan CPU: 0% (demo selesai memuat semua panelnya dan sebagian besar menganggur) ... [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 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 afinitas CPU untuk proses "java" agar menggunakan satu CPU) ...

Tentu saja, saya dapat melihat nomor penggunaan yang sama melalui pengelola tugas, tetapi intinya di sini adalah saya sekarang memiliki cara terprogram untuk merekam data yang sama. Ini akan berguna untuk pengujian yang berjalan lama dan diagnostik aplikasi server. Pustaka lengkap (tersedia di Sumber) menambahkan beberapa metode asli berguna lainnya, termasuk satu untuk mendapatkan proses PID (untuk integrasi dengan alat eksternal).

Vladimir Roubtsov telah memprogram dalam berbagai bahasa selama lebih dari 12 tahun, termasuk Java sejak 1995. Saat ini, ia mengembangkan perangkat lunak perusahaan sebagai pengembang senior untuk Trilogi di Austin, Texas. Saat membuat kode untuk bersenang-senang, Vladimir mengembangkan perangkat lunak berdasarkan kode byte Java atau instrumentasi kode sumber.

Pelajari lebih lanjut tentang topik ini

  • Unduh perpustakaan 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 umum yang baik tentang JNI, lihat Pengembangan Komponen untuk Platform Java dari Stuart Dabbs Halloway (Addison-Wesley, Desember 2001; ISBN0201753065)

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

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

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

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

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

  • Selama lebih dari 100 kiat wawasan Jawa, kunjungan JavaWorld' s Java Tips halaman indeks

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

  • Jelajahi bagian Core Java dari Indeks Topikal JavaWorld

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

  • Dapatkan lebih banyak jawaban atas pertanyaan Anda dalam diskusi Pemula Java kami

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

  • Mendaftarlah untuk mendapatkan buletin email mingguan gratis JavaWorld

    //www.javaworld.com/subscribe

  • Anda akan menemukan banyak artikel terkait TI dari publikasi saudara kita di .net

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