Προγραμματισμός

Προγραμματισμός χρήσης CPU από μια εφαρμογή Java

8 Νοεμβρίου 2002

Ε: Πώς καθορίζετε τη χρήση της CPU στην Java;

ΕΝΑ: Λοιπόν, εδώ είναι τα καλά νέα και τα κακά νέα. Τα κακά νέα είναι ότι το ερώτημα μέσω προγραμματισμού για χρήση της CPU είναι αδύνατο χρησιμοποιώντας καθαρή Java. Απλώς δεν υπάρχει API για αυτό. Μια προτεινόμενη εναλλακτική λύση μπορεί να χρησιμοποιηθεί Runtime.exec () για να προσδιορίσετε το αναγνωριστικό διεργασίας (PID) του JVM, καλέστε μια εξωτερική εντολή για συγκεκριμένη πλατφόρμα όπως ΥΣΤΕΡΟΓΡΑΦΟ, και αναλύστε τα αποτελέσματά του για το PID ενδιαφέροντος. Όμως, αυτή η προσέγγιση είναι εύθραυστη στην καλύτερη περίπτωση.

Τα καλά νέα, ωστόσο, είναι ότι μια αξιόπιστη λύση μπορεί να επιτευχθεί βγαίνοντας εκτός Java και γράφοντας μερικές γραμμές κώδικα C που ενσωματώνονται στην εφαρμογή Java μέσω Java Native Interface (JNI). Δείχνω παρακάτω πόσο εύκολο είναι δημιουργώντας μια απλή βιβλιοθήκη JNI για την πλατφόρμα Win32. Η ενότητα Πόροι περιέχει έναν σύνδεσμο προς τη βιβλιοθήκη που μπορείτε να προσαρμόσετε για τις δικές σας ανάγκες και να μεταφέρετε σε άλλες πλατφόρμες.

Σε γενικές γραμμές, το JNI είναι κάπως περίπλοκο στη χρήση. Ωστόσο, όταν καλείτε μόνο προς μία κατεύθυνση - από Java σε εγγενή κώδικα - και επικοινωνείτε χρησιμοποιώντας πρωτόγονους τύπους δεδομένων, τα πράγματα παραμένουν απλά. Υπάρχουν πολλές καλές αναφορές (βλ. Πόροι) στο JNI, οπότε δεν παρέχω εδώ ένα σεμινάριο JNI. Περιγράφω απλώς τα βήματα εφαρμογής μου.

Ξεκινώ δημιουργώντας μια τάξη com.vladium.utils.SystemInformation που δηλώνει μια εγγενή μέθοδο, η οποία επιστρέφει τον αριθμό χιλιοστών του δευτερολέπτου του χρόνου CPU που χρησιμοποιείται από την τρέχουσα διαδικασία μέχρι στιγμής:

 δημόσιο στατικό εγγενές long getProcessCPUTime (); 

Χρησιμοποιώ το εργαλείο javah από το JDK για να δημιουργήσω την ακόλουθη κεφαλίδα C για τη μελλοντική εγγενή εφαρμογή μου:

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

Στις περισσότερες πλατφόρμες Win32, αυτή η μέθοδος μπορεί να εφαρμοστεί χρησιμοποιώντας το GetProcessTimes () κλήση συστήματος και είναι κυριολεκτικά τρεις γραμμές κώδικα 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); return (jlong) ((fileTimeToInt64 (& kernelTime) + fileTimeToInt64 (& userTime)) / (s_numberOfProcessors * 10000)); } 

Αυτή η μέθοδος προσθέτει χρόνο CPU που δαπανάται για την εκτέλεση του πυρήνα και του κωδικού χρήστη για λογαριασμό της τρέχουσας διαδικασίας, ομαλοποιεί τον αριθμό επεξεργαστών και μετατρέπει το αποτέλεσμα σε χιλιοστά του δευτερολέπτου. ο fileTimeToInt64 () είναι μια βοηθητική συνάρτηση που μετατρέπει το ΑΡΧΕΙΟ δομή σε ακέραιο αριθμό 64-bit, και s_currentProcess και s_numberOfProcessors είναι καθολικές μεταβλητές που μπορούν να αρχικοποιηθούν εύκολα με μια μέθοδο JNI που καλείται μία φορά όταν το JVM φορτώνει την εγγενή βιβλιοθήκη:

στατικό HANDLE s_currentProcess; στατικός int s_numberOfProcessors; JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * δεσμευμένο) {SYSTEM_INFO systemInfo; s_currentProcess = GetCurrentProcess (); GetSystemInfo (& systemInfo); s_numberOfProcessors = systemInfo.dwNumberOfProcessors; επιστροφή JNI_VERSION_1_2; } 

Σημειώστε ότι εάν το εφαρμόσετε getProcessCPUTime () σε μια πλατφόρμα Unix, πιθανότατα θα χρησιμοποιήσετε το γοητεία κλήση συστήματος ως σημείο εκκίνησης.

Επιστροφή στην Java, φόρτωση της εγγενούς βιβλιοθήκης (silib.dll στο Win32) επιτυγχάνεται καλύτερα μέσω του στατικού αρχικοποιητή στο Πληροφορίες συστήματος τάξη:

 ιδιωτικό στατικό τελικό String SILIB = "silib"; στατική {δοκιμή {System.loadLibrary (SILIB); } catch (UnsatisfiedLinkError e) {System.out.println ("εγγενής lib '" + SILIB + "' δεν βρέθηκε στο 'java.library.path':" + System.getProperty ("java.library.path")); ρίξτε e; // εκ νέου ρίψη}} 

Σημειώστε ότι getProcessCPUTime () επιστρέφει το χρόνο CPU που χρησιμοποιήθηκε από τη δημιουργία της διαδικασίας JVM. Από μόνη της, αυτά τα δεδομένα δεν είναι ιδιαίτερα χρήσιμα για τη δημιουργία προφίλ. Χρειάζομαι περισσότερες βοηθητικές μεθόδους Java για να καταγράφω στιγμιότυπα δεδομένων σε διάφορες χρονικές στιγμές και να αναφέρω τη χρήση της CPU μεταξύ δύο χρονικών σημείων:

 δημόσια στατική τελική τάξη CPUUsageSnapshot {private CPUUsageSnapshot (long time, long CPUTime) {m_time = time; m_CPUTime = CPUTime; } δημόσιο τελικό μεγάλο m_time, m_CPUTime; } // τέλος της ένθετης δημόσιας στατικής CPUUsageSnapshot makeCPUUsageSnapshot () {return new CPUUsageSnapshot (System.currentTimeMillis (), getProcessCPUTime ()); } δημόσια στατική διπλή getProcessCPUUsage (CPUUsageSnapshot start, CPUUsageSnapshot end) {return ((double) (end.m_CPUTime - start.m_CPUTime)) / (end.m_time - start.m_time); } 

Το "CPU monitor API" είναι σχεδόν έτοιμο για χρήση! Ως τελική πινελιά, δημιουργώ μια τάξη με νήματα singleton, CPUUsageThread, η οποία λαμβάνει αυτόματα στιγμιότυπα δεδομένων σε τακτά χρονικά διαστήματα (0,5 δευτερόλεπτα από προεπιλογή) και τα αναφέρει σε ένα σύνολο ακροατών συμβάντων χρήσης CPU (το γνωστό μοτίβο Observer). ο CPUmon class είναι ένας ακροατής επίδειξης που απλά εκτυπώνει τη χρήση της CPU System.out:

 public static void main (String [] args) ρίχνει την Εξαίρεση {if (args.length == 0) ρίξτε νέο IllegalArgumentException ("use: CPUmon"); CPUUsageThread monitor = CPUUsageThread.getCPUThreadUsageThread (); CPUmon _this = νέο CPUmon (); Class app = Class.forName (args [0]); Μέθοδος appmain = app.getMethod ("main", new Class [] {String []. Class}); String [] appargs = new String [args.length - 1]; System.arraycopy (args, 1, appargs, 0, appargs.length); monitor.addUsageEventListener (_αυτό); monitor.start (); appmain.invoke (μηδέν, νέο αντικείμενο [] {appargs}); } 

Επιπροσθέτως, CPUmon.main () "αναδιπλώνει" μια άλλη κύρια τάξη Java με μοναδικό σκοπό την εκκίνηση CPUUsageThread πριν ξεκινήσετε την αρχική εφαρμογή.

Ως επίδειξη, έτρεξα CPUmon με το SwingSet2 Swing demo από το JDK 1.3.1 (μην ξεχάσετε να το εγκαταστήσετε silib.dll σε μια τοποθεσία που καλύπτεται είτε από το ΜΟΝΟΠΑΤΙ Μεταβλητή περιβάλλοντος OS ή το java.library.path Ιδιότητα Java):

> java -Djava.library.path =. -cp silib.jar; (my JDK install dir) \ demo \ jfc \ SwingSet2 \ SwingSet2.jar CPUmon SwingSet2 [PID: 339] Χρήση CPU: 46,8% [PID: 339] Χρήση CPU: 51,4% [PID: 339] CPU χρήση: 54,8% (κατά τη φόρτωση, η επίδειξη χρησιμοποιεί σχεδόν το 100% μιας από τις δύο CPU στο μηχάνημά μου) ... [PID: 339] Χρήση CPU: 46.8% [PID: 339] Χρήση CPU: 0% [PID: 339] Χρήση CPU: 0% (η επίδειξη ολοκλήρωσε τη φόρτωση όλων των πινάκων της και είναι κυρίως αδρανής) ... [PID: 339] Χρήση CPU: 100% [PID: 339] Χρήση CPU: 98.4% [PID: 339] CPU χρήση: 97% (Επέστρεψα στον πίνακα ColorChooserDemo που έτρεξε μια κινούμενη εικόνα εντάσεως CPU που χρησιμοποίησε και τους δύο CPU μου) ... [PID: 339] Χρήση CPU: 81,4% [PID: 339] Χρήση CPU: 50% [PID : 339] Χρήση CPU: 50% (Χρησιμοποίησα το Windows NT Task Manager για να προσαρμόσω τη συνάφεια της CPU για τη διαδικασία "java" για χρήση μίας CPU) ... 

Φυσικά, μπορώ να παρακολουθήσω τους ίδιους αριθμούς χρήσης μέσω του διαχειριστή εργασιών, αλλά το σημείο εδώ είναι ότι έχω τώρα έναν προγραμματικό τρόπο εγγραφής των ίδιων δεδομένων. Θα είναι χρήσιμο για μακροχρόνιες δοκιμές και διαγνωστικά εφαρμογών διακομιστή. Η πλήρης βιβλιοθήκη (διαθέσιμη στους πόρους) προσθέτει μερικές άλλες χρήσιμες εγγενείς μεθόδους, συμπεριλαμβανομένης μιας για τη λήψη της διαδικασίας PID (για ενσωμάτωση με εξωτερικά εργαλεία).

Ο Vladimir Roubtsov έχει προγραμματίσει σε μια ποικιλία γλωσσών για περισσότερα από 12 χρόνια, συμπεριλαμβανομένης της Java από το 1995. Επί του παρόντος, αναπτύσσει εταιρικό λογισμικό ως ανώτερος προγραμματιστής της Trilogy στο Ώστιν του Τέξας. Όταν κωδικοποιεί για διασκέδαση, το Vladimir αναπτύσσει εργαλεία λογισμικού που βασίζονται σε κώδικα Java byte ή όργανα πηγαίου κώδικα.

Μάθετε περισσότερα σχετικά με αυτό το θέμα

  • Κατεβάστε την πλήρη βιβλιοθήκη που συνοδεύει αυτό το άρθρο

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

  • JNI προδιαγραφές και σεμινάρια

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

  • Για μια καλή επισκόπηση του JNI, δείτε το Stuart Dabbs Halloway's Ανάπτυξη στοιχείων για την πλατφόρμα Java (Addison-Wesley, Δεκέμβριος 2001 · ISBN0201753065)

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

  • Στην «Συμβουλή Java 92 Χρησιμοποιήστε τη διασύνδεση Profiler JVM για ακριβή χρονισμό», ο Jesper Gortz διερευνά μια εναλλακτική κατεύθυνση για τη δημιουργία προφίλ στη χρήση του CPU (Ωστόσο, η χρήση του JVMPI απαιτεί περισσότερη δουλειά για τον υπολογισμό της χρήσης της CPU για ολόκληρη τη διαδικασία σε σύγκριση με τη λύση αυτού του άρθρου)

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

  • Δείτε το Ε & Α Java σελίδα ευρετηρίου για τον πλήρη κατάλογο ερωτήσεων και απαντήσεων

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

  • Για περισσότερες από 100 χρήσιμες συμβουλές Java, επισκεφτείτε τη διεύθυνση JavaWorld 'μικρό Συμβουλές Java σελίδα ευρετηρίου

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

  • Περιηγηθείτε στο Πυρήνας Java τμήμα του JavaWorld 'Τοπ Ευρετήριο

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

  • Λάβετε περισσότερες από τις ερωτήσεις σας απαντημένες στο Αρχάριος Java συζήτηση

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

  • Εγγραφείτε JavaWorldΔωρεάν εβδομαδιαία ενημερωτικά δελτία email

    //www.javaworld.com/subscribe

  • Θα βρείτε πληθώρα άρθρων που σχετίζονται με την πληροφορική από τις αδελφές εκδόσεις μας στο .net

Αυτή η ιστορία, "Προγραμματισμός χρήσης CPU από μια εφαρμογή Java" δημοσιεύθηκε αρχικά από την JavaWorld.

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